Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions js/module.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ export interface IVideo {
destroy(): void;
readonly skippedFrames: number;
readonly encodedFrames: number;
readonly canvasId: number;
}
export interface IVideoFactory {
create(): IVideo;
Expand Down Expand Up @@ -795,7 +796,6 @@ export interface IVideoEncoderFactory {
create(id: string, name: string, settings?: ISettings): IVideoEncoder;
}
export interface IStreaming {
// Video encoder value is only ignored in the Enhanced Broadcasting mode, otherwise it should be set
videoEncoder?: IVideoEncoder;
service: IService;
enforceServiceBitrate: boolean;
Expand All @@ -806,7 +806,7 @@ export interface IStreaming {
video: IVideo;
signalHandler: (signal: EOutputSignal) => void;
getAvailableEncoders(): IEncoderOption[];
start(): void; // throws
start(): void;
stop(force?: boolean): void;
droppedFrames: number;
totalFrames: number;
Expand Down Expand Up @@ -847,19 +847,15 @@ export interface IAdvancedStreamingFactory {
legacySettings: IAdvancedStreaming;
}
export interface IEnhancedBroadcastingAdvancedStreaming extends IAdvancedStreaming {
// If set, the Enhanced Broadcasting stream will be in the Dual Output mode.
// This value should be initialized before the stream start.
additionalVideo?: IVideo,
additionalVideo?: IVideo;
}
export interface IEnhancedBroadcastingAdvancedStreamingFactory {
create(): IEnhancedBroadcastingAdvancedStreaming;
destroy(stream: IEnhancedBroadcastingAdvancedStreaming): void;
legacySettings: IEnhancedBroadcastingAdvancedStreaming;
}
export interface IEnhancedBroadcastingSimpleStreaming extends ISimpleStreaming {
// If set, the Enhanced Broadcasting stream will be in the Dual Output mode.
// This value should be initialized before the stream start.
additionalVideo?: IVideo,
additionalVideo?: IVideo;
}
export interface IEnhancedBroadcastingSimpleStreamingFactory {
create(): IEnhancedBroadcastingSimpleStreaming;
Expand Down Expand Up @@ -981,6 +977,29 @@ export interface IAudioTrackFactory {
importLegacySettings(): void;
saveLegacySettings(): void;
}
export interface IAutoConfigResourcePercentile {
p50: number;
p95: number;
}
export interface IAutoConfigResourceGpu {
available: boolean;
vramUsedMB?: IAutoConfigResourcePercentile;
vramBudgetMB?: number;
}
export type AutoConfigResourcePhase = 'bandwidth' | 'stream_encoder' | 'recording_encoder';
export interface IAutoConfigResourceUsage {
phase: AutoConfigResourcePhase;
sampleCount: number;
durationMs: number;
cpuPct: IAutoConfigResourcePercentile;
procRamMB: IAutoConfigResourcePercentile;
gpu: IAutoConfigResourceGpu;
}
export interface IAutoConfigSummary {
complete: boolean;
resourceUsage: IAutoConfigResourceUsage[];
[key: string]: unknown;
}
export declare const enum VCamOutputType {
Invalid = 0,
SceneOutput = 1,
Expand Down
45 changes: 24 additions & 21 deletions js/module.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeObs = exports.getSourcesSize = exports.createSources = exports.addItems = exports.AdvancedReplayBufferFactory = exports.SimpleReplayBufferFactory = exports.AudioEncoderFactory = exports.AdvancedRecordingFactory = exports.SimpleRecordingFactory = exports.AudioTrackFactory = exports.NetworkFactory = exports.ReconnectFactory = exports.DelayFactory = exports.AdvancedStreamingFactory = exports.EnhancedBroadcastingSimpleStreamingFactory = exports.EnhancedBroadcastingAdvancedStreamingFactory = exports.SimpleStreamingFactory = exports.ServiceFactory = exports.VideoEncoderFactory = exports.IPC = exports.ModuleFactory = exports.AudioFactory = exports.Audio = exports.FaderFactory = exports.VolmeterFactory = exports.DisplayFactory = exports.TransitionFactory = exports.FilterFactory = exports.SceneFactory = exports.InputFactory = exports.VideoFactory = exports.Video = exports.Global = exports.DefaultPluginPathMac = exports.DefaultPluginDataPath = exports.DefaultPluginPath = exports.DefaultDataPath = exports.DefaultBinPath = exports.DefaultDrawPluginPath = exports.DefaultOpenGLPath = exports.DefaultD3D11Path = void 0;
exports.NodeObs = exports.AdvancedReplayBufferFactory = exports.SimpleReplayBufferFactory = exports.AudioEncoderFactory = exports.AdvancedRecordingFactory = exports.SimpleRecordingFactory = exports.AudioTrackFactory = exports.NetworkFactory = exports.ReconnectFactory = exports.DelayFactory = exports.EnhancedBroadcastingSimpleStreamingFactory = exports.EnhancedBroadcastingAdvancedStreamingFactory = exports.AdvancedStreamingFactory = exports.SimpleStreamingFactory = exports.ServiceFactory = exports.VideoEncoderFactory = exports.IPC = exports.ModuleFactory = exports.AudioFactory = exports.Audio = exports.FaderFactory = exports.VolmeterFactory = exports.DisplayFactory = exports.TransitionFactory = exports.FilterFactory = exports.SceneFactory = exports.InputFactory = exports.VideoFactory = exports.Video = exports.Global = exports.DefaultPluginPathMac = exports.DefaultPluginDataPath = exports.DefaultPluginPath = exports.DefaultDataPath = exports.DefaultBinPath = exports.DefaultDrawPluginPath = exports.DefaultOpenGLPath = exports.DefaultD3D11Path = void 0;
exports.addItems = addItems;
exports.createSources = createSources;
exports.getSourcesSize = getSourcesSize;
const path = require("path");
const fs = require("fs");
// Mac- search for optional OSN.app bundle (Chromium requires an app bundle to find obs64 helper apps)
const hasDeveloperApp = process.platform === 'darwin' && fs.existsSync(path.join(__dirname, 'OSN.app'));
const obs = hasDeveloperApp
? require('./OSN.app/distribute/obs-studio-node/obs_studio_client.node')
: require('./obs_studio_client.node');
? require('./OSN.app/distribute/obs-studio-node/obs_studio_client.node')
: require('./obs_studio_client.node');
exports.DefaultD3D11Path = path.resolve(__dirname, `libobs-d3d11.dll`);
exports.DefaultOpenGLPath = path.resolve(__dirname, `libobs-opengl.dll`);
exports.DefaultDrawPluginPath = path.resolve(__dirname, `simple_draw.dll`);
Expand Down Expand Up @@ -60,56 +62,57 @@ function addItems(scene, sceneItems) {
}
return items;
}
exports.addItems = addItems;
function createSources(sources) {
const items = [];
if (Array.isArray(sources)) {
sources.forEach(function (source) {
var _a, _b, _c;
let newSource = null;
try {
newSource = obs.Input.create(source.type, source.name, source.settings);
} catch (error) {
}
catch (error) {
console.error(`[OSN] Failed to create input for source "${source.name}":`, error instanceof Error ? error.message : error);
return; // Skip the rest of this iteration if input creation fails
return;
}

if (newSource) {
if (newSource.audioMixers) {
newSource.muted = source.muted ?? false;
newSource.volume = source.volume ?? 1;
newSource.syncOffset = source.syncOffset ?? { sec: 0, nsec: 0 };
newSource.muted = (_a = source.muted) !== null && _a !== void 0 ? _a : false;
newSource.volume = (_b = source.volume) !== null && _b !== void 0 ? _b : 1;
newSource.syncOffset = (_c = source.syncOffset) !== null && _c !== void 0 ? _c : { sec: 0, nsec: 0 };
}
newSource.deinterlaceMode = source.deinterlaceMode;
newSource.deinterlaceFieldOrder = source.deinterlaceFieldOrder;
items.push(newSource);

const filters = source.filters;
if (Array.isArray(filters)) {
filters.forEach(function (filter) {
var _a;
let ObsFilter = null;
try {
ObsFilter = obs.Filter.create(filter.type, filter.name, filter.settings);
} catch (filterError) {
}
catch (filterError) {
console.error(`[OSN] Failed to create filter "${filter.name}" for source "${source.name}":`, filterError instanceof Error ? filterError.message : filterError);
}

if (ObsFilter) {
ObsFilter.enabled = filter.enabled ?? true;
ObsFilter.enabled = (_a = filter.enabled) !== null && _a !== void 0 ? _a : true;
newSource.addFilter(ObsFilter);
ObsFilter.release();
}
});
}
} else {
}
else {
console.warn(`[OSN] Input creation failed for source: ${source.name}`);
}
});
} else {
}
else {
console.error(`[OSN] Invalid sources array provided:`, sources);
}
return items;
}
exports.createSources = createSources;
function getSourcesSize(sourcesNames) {
const sourcesSize = [];
if (Array.isArray(sourcesNames)) {
Expand All @@ -122,10 +125,10 @@ function getSourcesSize(sourcesNames) {
}
return sourcesSize;
}
exports.getSourcesSize = getSourcesSize;
;
const appleBinaryFolder = hasDeveloperApp
? path.join(__dirname, 'OSN.app', 'distribute', 'obs-studio-node', 'bin')
: path.join(__dirname, 'bin');
? path.join(__dirname, 'OSN.app', 'distribute', 'obs-studio-node', 'bin')
: path.join(__dirname, 'bin');
if (fs.existsSync(path.resolve(appleBinaryFolder, 'obs64').replace('app.asar', 'app.asar.unpacked'))) {
obs.IPC.setServerPath(path.resolve(appleBinaryFolder, `obs64`).replace('app.asar', 'app.asar.unpacked'), path.resolve(appleBinaryFolder).replace('app.asar', 'app.asar.unpacked'));
}
Expand Down
47 changes: 47 additions & 0 deletions js/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,11 @@ export interface IVideo {
* Number of total encoded frames
*/
readonly encodedFrames: number;

/**
* Server-side canvas id. Pass to APIs that reference video contexts by id.
*/
readonly canvasId: number;
}

export interface IVideoFactory {
Expand Down Expand Up @@ -1920,6 +1925,48 @@ export interface IAudioTrackFactory {
saveLegacySettings(): void;
}

// ---- Autoconfig resource-usage telemetry ----
//
// Shapes for the JSON payload of the autoconfig 'resource_usage' event, and
// the matching `resourceUsage` array inside GetAutoConfigSummary()'s JSON.
//
// p50 is the typical value during the phase; p95 is the sustained ceiling
// after dropping single-sample spikes from unrelated OS noise. min / max / avg
// are deliberately not exposed — max overweights one-off background activity
// and avg is hard to act on.

export interface IAutoConfigResourcePercentile {
p50: number;
p95: number;
}

export interface IAutoConfigResourceGpu {
available: boolean;
vramUsedMB?: IAutoConfigResourcePercentile;
vramBudgetMB?: number;
}

export type AutoConfigResourcePhase = 'bandwidth' | 'stream_encoder' | 'recording_encoder';

export interface IAutoConfigResourceUsage {
phase: AutoConfigResourcePhase;
sampleCount: number;
durationMs: number;
cpuPct: IAutoConfigResourcePercentile;
procRamMB: IAutoConfigResourcePercentile;
gpu: IAutoConfigResourceGpu;
}

// Parsed shape of NodeObs.GetAutoConfigSummary(). Only the fields the
// resource-usage feature consumes are typed; other historical fields
// (encoderDetection, videoDecision, bandwidthTest, selection) are present in
// the JSON but intentionally left as `unknown` — type them when you need them.
export interface IAutoConfigSummary {
complete: boolean;
resourceUsage: IAutoConfigResourceUsage[];
[key: string]: unknown;
}

export const enum VCamOutputType {
Invalid,
SceneOutput,
Expand Down
1 change: 0 additions & 1 deletion js/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"noImplicitAny": true,
"noImplicitThis": true,
"removeComments": true,
"suppressImplicitAnyIndexErrors": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": false,
"emitDecoratorMetadata": true
Expand Down
23 changes: 11 additions & 12 deletions js/type_check.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEmptyProperty = exports.isFontProperty = exports.isCaptureProperty = exports.isColorProperty = exports.isButtonProperty = exports.isBooleanProperty = exports.isEditableListProperty = exports.isListProperty = exports.isPathProperty = exports.isTextProperty = exports.isNumberProperty = void 0;
exports.isNumberProperty = isNumberProperty;
exports.isTextProperty = isTextProperty;
exports.isPathProperty = isPathProperty;
exports.isListProperty = isListProperty;
exports.isEditableListProperty = isEditableListProperty;
exports.isBooleanProperty = isBooleanProperty;
exports.isButtonProperty = isButtonProperty;
exports.isColorProperty = isColorProperty;
exports.isCaptureProperty = isCaptureProperty;
exports.isFontProperty = isFontProperty;
exports.isEmptyProperty = isEmptyProperty;
function isNumberProperty(property) {
return property.type === 2 ||
property.type === 3;
}
exports.isNumberProperty = isNumberProperty;
function isTextProperty(property) {
return property.type === 4;
}
exports.isTextProperty = isTextProperty;
function isPathProperty(property) {
return property.type === 5;
}
exports.isPathProperty = isPathProperty;
function isListProperty(property) {
return property.type === 6;
}
exports.isListProperty = isListProperty;
function isEditableListProperty(property) {
return property.type === 10;
}
exports.isEditableListProperty = isEditableListProperty;
function isBooleanProperty(property) {
return property.type === 1;
}
exports.isBooleanProperty = isBooleanProperty;
function isButtonProperty(property) {
return property.type === 8;
}
exports.isButtonProperty = isButtonProperty;
function isColorProperty(property) {
return property.type === 7;
}
exports.isColorProperty = isColorProperty;
function isCaptureProperty(property) {
return property.type === 14;
}
exports.isCaptureProperty = isCaptureProperty;
function isFontProperty(property) {
return property.type === 9;
}
exports.isFontProperty = isFontProperty;
function isEmptyProperty(property) {
switch (property.type) {
case 1:
Expand All @@ -53,4 +53,3 @@ function isEmptyProperty(property) {
}
return false;
}
exports.isEmptyProperty = isEmptyProperty;
Loading
Loading