Skip to content

Commit

Permalink
Obs renderer (stream-labs#906)
Browse files Browse the repository at this point in the history
* project: Directly access OBS in Renderer processes

As we now have our own IPC framework, we no longer have to rely on electron's IPC framework to do our work for us. We can directly call things from any process, as long as that process is actually connected to the IPC server.

For OBS IPC, this means a lot of data can be directly accessed without blocking both the renderer and main process, thus freeing up resources for more responsiveness and faster rendering. As a side effect, we might see lower CPU usage too.

* app: Ensure environment variable is valid

* obs-api: Fix formatting

* main: Fix working directory and path.join

* services/app: Fix shutdown crash and ensure env variable is valid
  • Loading branch information
eddyStreamlabs authored Oct 12, 2018
1 parent aaf0e21 commit 84ea031
Show file tree
Hide file tree
Showing 19 changed files with 117 additions and 268 deletions.
14 changes: 9 additions & 5 deletions app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Vue from 'vue';
import URI from 'urijs';

import { createStore } from './store';
import { ObsApiService } from './services/obs-api';
import { IWindowOptions, WindowsService } from './services/windows';
import { AppService } from './services/app';
import { ServicesManager } from './services-manager';
Expand All @@ -27,10 +26,18 @@ import ChildWindow from 'components/windows/ChildWindow.vue';
import OneOffWindow from 'components/windows/OneOffWindow.vue';

const { ipcRenderer, remote } = electron;

const slobsVersion = remote.process.env.SLOBS_VERSION;
const isProduction = process.env.NODE_ENV === 'production';

window['obs'] = window['require']('obs-studio-node');

{ // Set up things for IPC
// Connect to the IPC Server
window['obs'].IPC.connect(remote.process.env.SLOBS_IPC_PATH);
document.addEventListener('close', (e) => {
window['obs'].IPC.disconnect();
});
}

// This is the development DSN
let sentryDsn = 'https://[email protected]/252950';
Expand Down Expand Up @@ -102,7 +109,6 @@ document.addEventListener('DOMContentLoaded', () => {
const servicesManager: ServicesManager = ServicesManager.instance;
const windowsService: WindowsService = WindowsService.instance;
const i18nService: I18nService = I18nService.instance;
const obsApiService = ObsApiService.instance;
const windowId = Utils.getCurrentUrlParams().windowId;

if (Utils.isMainWindow()) {
Expand All @@ -115,8 +121,6 @@ document.addEventListener('DOMContentLoaded', () => {
servicesManager.listenMessages();
}

window['obs'] = obsApiService.nodeObs;

storePromise.then(async store => {

Vue.use(VueI18n);
Expand Down
2 changes: 0 additions & 2 deletions app/services-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { Hotkey, HotkeysService } from './services/hotkeys';
import { KeyListenerService } from './services/key-listener';
import { NavigationService } from './services/navigation';
import { NotificationsService } from './services/notifications';
import { ObsApiService } from './services/obs-api';
import { OnboardingService } from './services/onboarding';
import { PerformanceService } from './services/performance';
import { PerformanceMonitorService } from './services/performance-monitor';
Expand Down Expand Up @@ -126,7 +125,6 @@ export class ServicesManager extends Service {
KeyListenerService,
NavigationService,
NotificationsService,
ObsApiService,
OnboardingService,
PerformanceService,
PerformanceMonitorService,
Expand Down
6 changes: 6 additions & 0 deletions app/services/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { FileManagerService } from 'services/file-manager';
import { PatchNotesService } from 'services/patch-notes';
import { ProtocolLinksService } from 'services/protocol-links';
import { WindowsService } from 'services/windows';
import * as obs from '../../../obs-api';
import { FacemasksService } from 'services/facemasks';
import { OutageNotificationsService } from 'services/outage-notifications';
import { CrashReporterService } from 'services/crash-reporter';
Expand Down Expand Up @@ -72,6 +73,9 @@ export class AppService extends StatefulService<IAppState> {
async load() {
this.START_LOADING();

// Initialize OBS
obs.NodeObs.OBS_API_initAPI('en-US', electron.remote.process.env.SLOBS_IPC_USERDATA);

// We want to start this as early as possible so that any
// exceptions raised while loading the configuration are
// associated with the user in sentry.
Expand Down Expand Up @@ -134,6 +138,8 @@ export class AppService extends StatefulService<IAppState> {
this.windowsService.closeAllOneOffs();
await this.fileManagerService.flushAll();
this.crashReporterService.endShutdown();
obs.NodeObs.OBS_service_removeCallback();
obs.NodeObs.OBS_API_destroyOBS_API();
electron.ipcRenderer.send('shutdownComplete');
}, 300);
}
Expand Down
1 change: 1 addition & 0 deletions app/services/audio/audio-api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import * as obs from '../../../obs-api';
import { Subscription } from 'rxjs/Subscription';
import { TObsFormData } from 'components/obs/inputs/ObsInput';
Expand Down
22 changes: 11 additions & 11 deletions app/services/auto-config/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Service } from '../service';
import { nodeObs } from '../obs-api';
import * as obs from '../../../obs-api';
import { continentMap } from './continent-map';

export type TConfigEvent =
Expand Down Expand Up @@ -27,7 +27,7 @@ export class AutoConfigService extends Service {

start(cb: TConfigProgressCallback) {
this.fetchLocation(cb).then(continent => {
nodeObs.InitializeAutoConfig(
obs.NodeObs.InitializeAutoConfig(
(progress: IConfigProgress) => {
this.handleProgress(progress);
cb(progress);
Expand All @@ -38,33 +38,33 @@ export class AutoConfigService extends Service {
}
);

nodeObs.StartBandwidthTest();
obs.NodeObs.StartBandwidthTest();
});
}

handleProgress(progress: IConfigProgress) {
if (progress.event === 'stopping_step') {
if (progress.description === 'bandwidth_test') {
nodeObs.StartStreamEncoderTest();
obs.NodeObs.StartStreamEncoderTest();
} else if (progress.description === 'streamingEncoder_test') {
nodeObs.StartRecordingEncoderTest();
obs.NodeObs.StartRecordingEncoderTest();
} else if (progress.description === 'recordingEncoder_test') {
nodeObs.StartCheckSettings();
obs.NodeObs.StartCheckSettings();
} else if (progress.description === 'checking_settings') {
nodeObs.StartSaveStreamSettings();
obs.NodeObs.StartSaveStreamSettings();
} else if (progress.description === 'saving_service') {
nodeObs.StartSaveSettings();
obs.NodeObs.StartSaveSettings();
} else if (progress.description === 'setting_default_settings') {
nodeObs.StartSaveStreamSettings();
obs.NodeObs.StartSaveStreamSettings();
}
}

if (progress.event === 'error') {
nodeObs.StartSetDefaultSettings();
obs.NodeObs.StartSetDefaultSettings();
}

if (progress.event === 'done') {
nodeObs.TerminateAutoConfig();
obs.NodeObs.TerminateAutoConfig();
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/services/facemasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { SourceFiltersService } from './source-filters';
import { Inject } from 'util/injector';
import { handleErrors, authorizedHeaders } from 'util/requests';
import { mutation } from './stateful-service';
import * as obs from './obs-api';
import * as obs from '../../obs-api';
import path from 'path';
import fs from 'fs';
import https from 'https';
Expand Down
64 changes: 0 additions & 64 deletions app/services/obs-api.ts

This file was deleted.

6 changes: 3 additions & 3 deletions app/services/obs-importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { TransitionsService, ETransitionType } from 'services/transitions';
import { AudioService } from 'services/audio';
import { Inject } from 'util/injector';
import { SceneCollectionsService } from 'services/scene-collections';
import { nodeObs } from 'services/obs-api';
import * as obs from '../../obs-api';
import { SettingsService } from 'services/settings';

interface Source {
Expand Down Expand Up @@ -100,8 +100,8 @@ export class ObsImporterService extends Service {
}
}

nodeObs.OBS_service_resetVideoContext();
nodeObs.OBS_service_resetAudioContext();
obs.NodeObs.OBS_service_resetVideoContext();
obs.NodeObs.OBS_service_resetAudioContext();

// Ensure we reload any updated settings
this.settingsService.loadSettingsIntoStore();
Expand Down
28 changes: 12 additions & 16 deletions app/services/performance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Vue from 'vue';
import { Subject } from 'rxjs/Subject';

import { StatefulService, mutation } from './stateful-service';
import { nodeObs } from './obs-api';
import electron from 'electron';
import * as obs from '../../obs-api';

interface IPerformanceState {
CPU: number;
Expand Down Expand Up @@ -39,25 +39,21 @@ export class PerformanceService extends StatefulService<IPerformanceState> {
}

init() {
electron.ipcRenderer.on('notifyPerformanceStatistics', (e: Electron.Event, stats: IPerformanceState) => {
this.processPerformanceStats(stats);
});

this.intervalId = window.setInterval(() => {
electron.ipcRenderer.send('requestPerformanceStatistics');
}, STATS_UPDATE_INTERVAL);
}
const stats: IPerformanceState =
obs.NodeObs.OBS_API_getPerformanceStatistics();
if (stats.percentageDroppedFrames) {
this.droppedFramesDetected.next(stats.percentageDroppedFrames / 100);
}

processPerformanceStats(stats: IPerformanceState) {
if (stats.percentageDroppedFrames) {
this.droppedFramesDetected.next(stats.percentageDroppedFrames / 100);
}
const am = electron.remote.app.getAppMetrics();

stats.CPU += electron.remote.app.getAppMetrics().map(proc => {
return proc.cpu.percentCPUUsage;
}).reduce((sum, usage) => sum + usage);
stats.CPU += am.map(proc => {
return proc.cpu.percentCPUUsage;
}).reduce((sum, usage) => sum + usage);

this.SET_PERFORMANCE_STATS(stats);
this.SET_PERFORMANCE_STATS(stats);
}, STATS_UPDATE_INTERVAL);
}

stop() {
Expand Down
2 changes: 1 addition & 1 deletion app/services/scene-collections/nodes/overlays/webcam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SceneItem } from '../../../scenes';
import { VideoService } from '../../../video';
import { SourcesService } from '../../../sources';
import { sortBy } from 'lodash';
import { IListProperty } from '../../../obs-api';
import { IListProperty } from '../../../../../obs-api';
import { ScalableRectangle } from '../../../../util/ScalableRectangle';

interface ISchema {
Expand Down
2 changes: 1 addition & 1 deletion app/services/scenes/scene-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { VideoService } from 'services/video';
import { ScalableRectangle, CenteringAxis } from 'util/ScalableRectangle';
import { Inject } from 'util/injector';
import { TObsFormData } from 'components/obs/inputs/ObsInput';
import * as obs from '../obs-api';
import * as obs from '../../../obs-api';

import {
IPartialSettings,
Expand Down
2 changes: 1 addition & 1 deletion app/services/scenes/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ISceneItemNode
} from './index';
import Utils from 'services/utils';
import * as obs from 'services/obs-api';
import * as obs from '../../../obs-api';
import { Inject } from 'util/injector';
import { SelectionService, Selection, TNodesList } from 'services/selection';
import { uniqBy } from 'lodash';
Expand Down
2 changes: 1 addition & 1 deletion app/services/scenes/scenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { SourcesService, ISource } from 'services/sources';
import electron from 'electron';
import { Subject } from 'rxjs/Subject';
import { Inject } from 'util/injector';
import * as obs from 'services/obs-api';
import * as obs from '../../../obs-api';
import { $t } from 'services/i18n';
import namingHelpers from 'util/NamingHelpers';
import uuid from 'uuid/v4';
Expand Down
8 changes: 4 additions & 4 deletions app/services/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
TObsFormData,
IObsListInput
} from 'components/obs/inputs/ObsInput';
import { nodeObs } from '../obs-api';
import * as obs from '../../../obs-api';
import { SourcesService } from 'services/sources';
import { Inject } from '../../util/injector';
import { AudioService, E_AUDIO_CHANNELS } from 'services/audio';
Expand Down Expand Up @@ -112,7 +112,7 @@ export class SettingsService extends StatefulService<ISettingsState>
}

getCategories(): string[] {
let categories = nodeObs.OBS_settings_getListCategories();
let categories = obs.NodeObs.OBS_settings_getListCategories();
categories = categories
.concat(['Scene Collections', 'Notifications', 'Appearance', 'Remote Control']);

Expand All @@ -132,7 +132,7 @@ export class SettingsService extends StatefulService<ISettingsState>

getSettingsFormData(categoryName: string): ISettingsSubCategory[] {
if (categoryName === 'Audio') return this.getAudioSettingsFormData();
const settings = nodeObs.OBS_settings_getSettings(categoryName);
const settings = obs.NodeObs.OBS_settings_getSettings(categoryName);

// Names of settings that are disabled because we
// have not implemented them yet.
Expand Down Expand Up @@ -336,7 +336,7 @@ export class SettingsService extends StatefulService<ISettingsState>
});
}

nodeObs.OBS_settings_saveSettings(categoryName, dataToSave);
obs.NodeObs.OBS_settings_saveSettings(categoryName, dataToSave);
this.SET_SETTINGS(
SettingsService.convertFormDataToState({ [categoryName]: settingsData })
);
Expand Down
Loading

0 comments on commit 84ea031

Please sign in to comment.