只依赖Electron和OBS-node的简单例子
yarn install
yarn start
-
那么就修改
package.json:{ "devDependencies": { "obs-studio-node": "file://C:/where/you/cloned/obs-studio-node/build/obs-studio-node-0.3.21-win64.tar.gz" } }
log文件在 osn-data\node-obs\logs.
源obs库 https://obsproject.com/
node封装的obs库(参考/抄了很多这个项目的测试类) https://github.com/stream-labs/obs-studio-node
vue版本的node_obs(参考/抄了很多这个项目的代码) https://github.com/stream-labs/streamlabs-obs
重要: 使用 Electron 时, 所有显示的内容都必须在主进程中完成, 不能在渲染国产中执行. 否则你会看到异常
Uncaught Error: Failed to host and connect.
TypeScript:
import * as osn from "obs-studio-node";
JavaScript:
const osn = require("obs-studio-node");
const { Subject } = require("rxjs"); // 这里可以使用uuid,如同streamlabs-obs里面的那样 osn.NodeObs.IPC.host("obs-studio-node-example"); // 设置工作目录 osn.NodeObs.SetWorkingDirectory(path.join(__dirname, "../../node_modules/obs-studio-node")); // OBS Studio 配置文件和目录 const obsDataPath = path.join(__dirname, "../../osn-data"); // 初始化 api 传入 locale, path 和 version const initResult = osn.NodeObs.OBS_API_initAPI("en-US", obsDataPath, "1.0.0"); if (initResult !== 0) { const errorReasons = { "-2": "DirectX could not be found on your system. Please install the latest version of DirectX for your machine here <https://www.microsoft.com/en-us/download/details.aspx?id=35?> and try again.", "-5": "Failed to initialize OBS. Your video drivers may be out of date, or Streamlabs OBS may not be supported on your system.", }; const errorMessage = errorReasons[initResult.toString()] || `An unknown error #${initResult} was encountered while initializing OBS.`; console.error("OBS init failure", errorMessage); // see below for this function shutdown(); throw Error(errorMessage); } const signals = new Subject() osn.NodeObs.OBS_service_connectOutputSignals((signalInfo) => { signals.next(signalInfo); });
function getAvailableValues(category, subcategory, parameter) { const categorySettings = osn.NodeObs.OBS_settings_getSettings(category).data; if (!categorySettings) { console.warn(`There is no category ${category} in OBS settings`); return []; } const subcategorySettings = categorySettings.find( (sub) => sub.nameSubCategory === subcategory, ); if (!subcategorySettings) { console.warn(`There is no subcategory ${subcategory} for OBS settings category ${category}`); return []; } const parameterSettings = subcategorySettings.parameters.find( (param) => param.name === parameter, ); if (!parameterSettings) { console.warn(`There is no parameter ${parameter} for OBS settings category ${category}.${subcategory}`); return []; } return parameterSettings.values.map((value) => Object.values(value)[0]); } function setSetting(category, parameter, value) { let oldValue; // Getting settings container const settings = osn.NodeObs.OBS_settings_getSettings(category).data; settings.forEach((subCategory) => { subCategory.parameters.forEach((param) => { if (param.name === parameter) { oldValue = param.currentValue; param.currentValue = value; } }); }); // Saving updated settings container if (value != oldValue) { osn.NodeObs.OBS_settings_saveSettings(category, settings); } } setSetting("Output", "Mode", "Simple"); const availableEncoders = getAvailableValues("Output", "Recording", "RecEncoder"); setSetting("Output", "RecEncoder", availableEncoders.slice(-1)[0] || "x264"); setSetting("Output", "FilePath", path.join(__dirname, "../videos")); setSetting("Output", "RecFormat", "mkv"); setSetting("Output", "VBitrate", 10000); // 10 Mbps setSetting("Video", "FPSCommon", 60);
大概是这样创建:
const scene = osn.SceneFactory.create("myScene");
然后添加一个源(source)并添加到场景(scene)中
const source = osn.InputFactory.create("image_source", "logo", { file: path.join(__dirname, "../assets/icons/favicon.png") }); const sceneItem = scene.add(source);
osn.InputFactory.create() 方法需要参数 id, name 和 settings.
id 只能是有限的几个,具体取值可以参考 obs-studio-node中的/tests/osn-tests/util/obs_enums.EOBSInputTypes
settings 对象 根据不同的类型,可设置的值也不同, 如 videos,可以设置looping: true,具体取值大概可以参考 obs-studio-node中的/tests/osn-tests/util/input_setting.ts , 但是感觉这里不全(streamlabs-obs代码中应该有完全版吧...).
可以根据name获取 场景(scene)和源(source),使用方法 [factory].fromName(name). 例子:
const myScene = osn.SceneFactory.fromName("myScene");
您可以修改场景项的属性,例如移动,缩放源或者置顶:
sceneItem.position = { x: 50, y: 50 }; sceneItem.scale = { x: 0.5, y: 0.7 }; sceneItem.moveTop();
osn.Global.setOutputSource(0, this.scene);
从两个场景(scenes)间进行转场, 必须先创建一个transition然后设置成output.
const transition = osn.TransitionFactory.create(transitionType, "myTransition", {}); transition.set(scene); osn.Global.setOutputSource(0, transition);
可以使用transition.set(scene)方法设置场景(scene),然后过渡到另一个场景,如下所示:
transition.start(300, scene);
function getNextSignalInfo() { return new Promise((resolve, reject) => { signals.pipe(first()).subscribe(signalInfo => resolve(signalInfo)); setTimeout(() => reject('Output signal timeout'), 30000); }); } let signalInfo; console.debug('Starting recording...'); osn.NodeObs.OBS_service_startRecording(); console.debug('Started?'); signalInfo = await getNextSignalInfo(); if (signalInfo.signal === 'Stop') { throw Error(signalInfo.error); } console.debug('Started signalInfo.type:', signalInfo.type, '(expected: "recording")'); console.debug('Started signalInfo.signal:', signalInfo.signal, '(expected: "start")'); console.debug('Started!');
let signalInfo; console.debug('Stopping recording...'); osn.NodeObs.OBS_service_stopRecording(); console.debug('Stopped?'); signalInfo = await getNextSignalInfo(); console.debug('On stop signalInfo.type:', signalInfo.type, '(expected: "recording")'); console.debug('On stop signalInfo.signal:', signalInfo.signal, '(expected: "stopping")'); signalInfo = await getNextSignalInfo(); console.debug('After stop signalInfo.type:', signalInfo.type, '(expected: "recording")'); console.debug('After stop signalInfo.signal:', signalInfo.signal, '(expected: "stop")'); console.debug('Stopped!');
大概这样创建一个:
const displayId = "myDisplay"; const displayWidth = 960; const displayHeight = 540; const resized = () => { const { width, height } = previewWindow.getContentBounds(); osn.NodeObs.OBS_content_resizeDisplay(displayId, width, height + 20); osn.NodeObs.OBS_content_setPaddingSize(displayId, 5); }; previewWindow = new BrowserWindow({ width: displayWidth, height: displayHeight, // if you use this, the window will automatically close // when the parent window is closed parent: parentWindow, useContentSize: true, }); previewWindow.on("close", () => { osn.NodeObs.OBS_content_destroyDisplay(displayId); previewWindow = undefined; }); previewWindow.on("resize", resized); osn.NodeObs.OBS_content_createSourcePreviewDisplay( previewWindow.getNativeWindowHandle(), "", // or use camera source Id here displayId, ); osn.NodeObs.OBS_content_setShouldDrawUI(displayId, false); osn.NodeObs.OBS_content_setPaddingColor(displayId, 255, 255, 255); resized();
必须要一个 BrowserWindow 然后obs在它上面绘制. 然后必须监听所有的resize 和 move 事件, 否则就画出边界.
可以设置 padding
osn.NodeObs.OBS_content_setPaddingSize(displayId, 5); osn.NodeObs.OBS_content_setPaddingColor(displayId, 255, 255, 255);
function shutdown() { try { osn.NodeObs.OBS_service_removeCallback(); osn.NodeObs.IPC.disconnect(); this.obsInitialized = false; } catch (e) { throw Error(`Exception when shutting down OBS process${e}`); } }