diff --git a/README.md b/README.md index 32ddbd3..0102b94 100644 --- a/README.md +++ b/README.md @@ -33,17 +33,18 @@ Most of the interesting things are located in [`obsRecorder.js`](./obsRecorder.j Change link to [obs-studio-node] in your `package.json` to include version of package built for MacOS: ``` - "obs-studio-node": "https://obsstudionodes3.streamlabs.com/obs-studio-node-0.5.215-iojs-v6.0.3-osx-release.tar.gz", + "obs-studio-node": "https://s3-us-west-2.amazonaws.com/obsstudionodes3.streamlabs.com/osn-0.9.5-release-osx.tar.gz", ``` -And execute `yarn`: +To get the latest url, clone [streamlabs-obs](https://github.com/stream-labs/streamlabs-obs), run `yarn install` and copy it from the output. + +Execute `yarn`: ```sh yarn install ``` -May be it will work, may be not. - +Start the app from a native terminal, not an integrated terminal like VSCode's. ## Use with your own build of [obs-studio-node] diff --git a/obsRecorder.js b/obsRecorder.js index 9b6c830..3d2c90b 100644 --- a/obsRecorder.js +++ b/obsRecorder.js @@ -1,9 +1,10 @@ const path = require('path'); const { Subject } = require('rxjs'); const { first } = require('rxjs/operators'); +const { byOS, OS } = require('./operating-systems'); const osn = require("obs-studio-node"); -const { BrowserWindow } = require('electron'); +const { v4: uuid } = require('uuid'); let obsInitialized = false; let scene = null; @@ -30,7 +31,7 @@ function initialize(win) { function initOBS() { console.debug('Initializing OBS...'); - osn.NodeObs.IPC.host('obs-studio-node-example'); // Usually some UUIDs go there + osn.NodeObs.IPC.host(`obs-studio-node-example-${uuid()}`); osn.NodeObs.SetWorkingDirectory(path.join(__dirname, 'node_modules', 'obs-studio-node')); const obsDataPath = path.join(__dirname, 'osn-data'); // OBS Studio configs and logs @@ -59,7 +60,7 @@ function initOBS() { console.debug('OBS initialized'); } -function configureOBS() { +function configureOBS() { console.debug('Configuring OBS'); setSetting('Output', 'Mode', 'Advanced'); const availableEncoders = getAvailableValues('Output', 'Recording', 'RecEncoder'); @@ -92,12 +93,19 @@ function getCameraSource() { console.debug('Trying to set up web camera...') // Setup input without initializing any device just to get list of available ones - const dummyInput = osn.InputFactory.create('dshow_input', 'video', { - audio_device_id: 'does_not_exist', - video_device_id: 'does_not_exist', + const dummyInput = byOS({ + [OS.Windows]: () => + osn.InputFactory.create('dshow_input', 'video', { + audio_device_id: 'does_not_exist', + video_device_id: 'does_not_exist', + }), + [OS.Mac]: () => + osn.InputFactory.create('av_capture_input', 'video', { + device: 'does_not_exist', + }) }); - const cameraItems = dummyInput.properties.get('video_device_id').details.items; + const cameraItems = dummyInput.properties.get(byOS({ [OS.Windows]: 'video_device_id', [OS.Mac]: 'device' })).details.items; dummyInput.release(); @@ -110,9 +118,16 @@ function getCameraSource() { cameraItems[0].selected = true; console.debug('cameraItems[0].name: ' + cameraItems[0].name); - const obsCameraInput = osn.InputFactory.create('dshow_input', 'video', { - video_device_id: deviceId, - }); + const obsCameraInput = byOS({ + [OS.Windows]: () => + osn.InputFactory.create('dshow_input', 'video', { + video_device_id: deviceId, + }), + [OS.Mac]: () => + osn.InputFactory.create('av_capture_input', 'video', { + device: deviceId, + }), + }) // It's a hack to wait a bit until device become initialized (maximum for 1 second) // If you know proper way how to determine whether camera is working and how to subscribe for any events from it, create a pull request @@ -142,7 +157,7 @@ function getCameraSource() { } function setupScene() { - const videoSource = osn.InputFactory.create('monitor_capture', 'desktop-video'); + const videoSource = osn.InputFactory.create(byOS({ [OS.Windows]: 'monitor_capture', [OS.Mac]: 'display_capture' }), 'desktop-video'); const { physicalWidth, physicalHeight, aspectRatio } = displayInfo(); @@ -195,18 +210,18 @@ function setupSources() { setSetting('Output', 'Track1Name', 'Mixed: all sources'); let currentTrack = 2; - getAudioDevices('wasapi_output_capture', 'desktop-audio').forEach(metadata => { + getAudioDevices(byOS({ [OS.Windows]: 'wasapi_output_capture', [OS.Mac]: 'coreaudio_output_capture' }), 'desktop-audio').forEach(metadata => { if (metadata.device_id === 'default') return; - const source = osn.InputFactory.create('wasapi_output_capture', 'desktop-audio', { device_id: metadata.device_id }); + const source = osn.InputFactory.create(byOS({ [OS.Windows]: 'wasapi_output_capture', [OS.Mac]: 'coreaudio_output_capture' }), 'desktop-audio', { device_id: metadata.device_id }); setSetting('Output', `Track${currentTrack}Name`, metadata.name); source.audioMixers = 1 | (1 << currentTrack-1); // Bit mask to output to only tracks 1 and current track osn.Global.setOutputSource(currentTrack, source); currentTrack++; }); - getAudioDevices('wasapi_input_capture', 'mic-audio').forEach(metadata => { + getAudioDevices(byOS({ [OS.Windows]: 'wasapi_input_capture', [OS.Mac]: 'coreaudio_input_capture' }), 'mic-audio').forEach(metadata => { if (metadata.device_id === 'default') return; - const source = osn.InputFactory.create('wasapi_input_capture', 'mic-audio', { device_id: metadata.device_id }); + const source = osn.InputFactory.create(byOS({ [OS.Windows]: 'wasapi_input_capture', [OS.Mac]: 'coreaudio_input_capture' }), 'mic-audio', { device_id: metadata.device_id }); setSetting('Output', `Track${currentTrack}Name`, metadata.name); source.audioMixers = 1 | (1 << currentTrack-1); // Bit mask to output to only tracks 1 and current track osn.Global.setOutputSource(currentTrack, source); @@ -350,7 +365,7 @@ function getAvailableValues(category, subcategory, parameter) { } const signals = new Subject(); - + function getNextSignalInfo() { return new Promise((resolve, reject) => { signals.pipe(first()).subscribe(signalInfo => resolve(signalInfo)); diff --git a/operating-systems.js b/operating-systems.js new file mode 100644 index 0000000..22cb8b2 --- /dev/null +++ b/operating-systems.js @@ -0,0 +1,22 @@ +// Modified from https://github.com/stream-labs/streamlabs-obs/blob/staging/app/util/operating-systems.ts + +const OS = { + Windows: 'win32', + Mac: 'darwin', +} + +function byOS(handlers) { + const handler = handlers[process.platform]; + + if (typeof handler === 'function') return handler(); + + return handler; +} + +function getOS() { + return process.platform +} + +module.exports.OS = OS +module.exports.byOS = byOS +module.exports.getOS = getOS diff --git a/package.json b/package.json index cf72797..6bd0ef1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "devDependencies": { "electron": "^6.0.3", "rxjs": "^6.4.0", + "uuid": "^8.3.0", "obs-studio-node": "https://obsstudionodes3.streamlabs.com/obs-studio-node-0.5.137-iojs-v6.0.3-release.tar.gz" } } diff --git a/yarn.lock b/yarn.lock index 38638d1..74f8575 100644 --- a/yarn.lock +++ b/yarn.lock @@ -940,6 +940,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" + integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"