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
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,99 @@ window.obsstudio.getScenes(function (scenes) {
})
```

#### Get scene item list
Permissions required: READ_USER
```js
/**
* @typedef {Object} SceneItem
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love to see transform (or at least position) information included for these, if that would be possible?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I built the feature as closely as possible to the existing WebSocket implementation, and that implementation does not include this.

I also don’t think this needs to be added directly to SceneItem:

With my implementation, you can first retrieve a scene item and then call getSceneItemTransform to get everything you might need: position, rotation, scale, bounds, and even whether it’s cropped (see screenshot 2 above).

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry I completely missed that getSceneItemTransform exposes all the scene items and not just the browser source itself!

* @property {number} id - unique scene item ID
* @property {string} name - name of the source
* @property {boolean} visible - whether the item is visible
* @property {boolean} locked - whether the item is locked
* @property {string} type - the source type
*/

/**
* @callback SceneItemListCallback
* @param {SceneItem[]} sceneItems
*/

/**
* @param {SceneItemListCallback} cb - The callback that receives the list of scene items in the current scene.
*/
window.obsstudio.getSceneItemList(function(sceneItems) {
console.log(sceneItems)
})
```

#### Get scene item transform
Permissions required: READ_USER
```js
/**
* @typedef {Object} Transform
* @property {Object} position - position of the item
* @property {number} position.x - x coordinate
* @property {number} position.y - y coordinate
* @property {number} rotation - rotation in degrees
* @property {Object} scale - scale of the item
* @property {number} scale.x - x scale factor
* @property {number} scale.y - y scale factor
* @property {number} alignment - alignment flags
* @property {number} boundsType - bounds type
* @property {number} boundsAlignment - bounds alignment
* @property {Object} bounds - bounds of the item
* @property {number} bounds.x - bounds width
* @property {number} bounds.y - bounds height
* @property {Object} crop - crop settings
* @property {number} crop.left - left crop
* @property {number} crop.top - top crop
* @property {number} crop.right - right crop
* @property {number} crop.bottom - bottom crop
*/

/**
* @callback TransformCallback
* @param {Transform} transform
*/

/**
* @param {TransformCallback} cb - The callback that receives the transform data.
* @param {string} sceneName - Name of the scene
* @param {number} sceneItemId - ID of the scene item
*/
window.obsstudio.getSceneItemTransform(function(transform) {
console.log(transform)
}, "SceneName", 123)
```

#### Get browser input settings
Permissions required: READ_USER
```js
/**
* @typedef {Object} BrowserInputSettings
* @property {string} inputKind - always "browser_source"
* @property {Object} inputSettings - browser source settings
* @property {number} inputSettings.height - height of the browser source
* @property {boolean} inputSettings.shutdown - shutdown setting
* @property {string} inputSettings.url - URL of the browser source
* @property {number} inputSettings.webpage_control_level - control level
* @property {number} inputSettings.width - width of the browser source
*/

/**
* @callback BrowserInputSettingsCallback
* @param {BrowserInputSettings} settings
*/

/**
* @param {BrowserInputSettingsCallback} cb - The callback that receives the browser input settings.
* @param {string} inputName - Name of the browser source
*/
window.obsstudio.getBrowserInputSettings(function(settings) {
console.log(settings)
}, "BrowserSourceName")
```

#### Get transitions
Permissions required: READ_USER
```js
Expand Down
13 changes: 7 additions & 6 deletions browser-app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,13 @@ void BrowserApp::OnBeforeCommandLineProcessing(const CefString &, CefRefPtr<CefC
#endif
}

std::vector<std::string> exposedFunctions = {"getControlLevel", "getCurrentScene", "getStatus",
"startRecording", "stopRecording", "startStreaming",
"stopStreaming", "pauseRecording", "unpauseRecording",
"startReplayBuffer", "stopReplayBuffer", "saveReplayBuffer",
"startVirtualcam", "stopVirtualcam", "getScenes",
"setCurrentScene", "getTransitions", "getCurrentTransition",
std::vector<std::string> exposedFunctions = {"getControlLevel", "getCurrentScene", "getStatus",
"startRecording", "stopRecording", "startStreaming",
"stopStreaming", "pauseRecording", "unpauseRecording",
"startReplayBuffer", "stopReplayBuffer", "saveReplayBuffer",
"startVirtualcam", "stopVirtualcam", "getScenes",
"setCurrentScene", "getSceneItemList", "getBrowserInputSettings",
"getSceneItemTransform", "getTransitions", "getCurrentTransition",
"setCurrentTransition"};

void BrowserApp::OnContextCreated(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame>, CefRefPtr<CefV8Context> context)
Expand Down
127 changes: 127 additions & 0 deletions browser-client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@
#include "drm-format.hpp"
#endif

struct scene_item_enum_data {
std::vector<nlohmann::json> *items;
};

static bool enum_scene_items(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
{
scene_item_enum_data *data = static_cast<scene_item_enum_data *>(param);

obs_source_t *source = obs_sceneitem_get_source(item);
if (!source)
return true;

const char *source_name = obs_source_get_name(source);
if (!source_name)
return true;

nlohmann::json item_json = {{"id", obs_sceneitem_get_id(item)},
{"name", source_name},
{"visible", obs_sceneitem_visible(item)},
{"locked", obs_sceneitem_locked(item)},
{"type", obs_source_get_type(source)}};

data->items->push_back(item_json);
return true;
}

inline bool BrowserClient::valid() const
{
return !!bs && !bs->destroying;
Expand Down Expand Up @@ -244,6 +270,107 @@ bool BrowserClient::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser, CefR
json = {{"name", name},
{"width", obs_source_get_width(current_scene)},
{"height", obs_source_get_height(current_scene)}};
} else if (name == "getSceneItemList") {
OBSSourceAutoRelease current_scene = obs_frontend_get_current_scene();

if (!current_scene)
return false;

// Convert source to scene object for enumeration
obs_scene_t *scene = obs_scene_from_source(current_scene);
if (!scene)
return false;

std::vector<nlohmann::json> scene_items;
scene_item_enum_data data = {&scene_items};

// Enumerate all items in the scene and collect their data
obs_scene_enum_items(scene, enum_scene_items, &data);

json = scene_items;
} else if (name == "getBrowserInputSettings") {
const std::string input_name = input_args->GetString(1).ToString();

// Retrieve the source by name from OBS
// Lookup by ID not needed. When a user duplicates a source with reference the settings are the same.
OBSSourceAutoRelease source = obs_get_source_by_name(input_name.c_str());
if (!source) {
return false;
}

// Validate that the source is actually a browser source
const char *source_id = obs_source_get_id(source);
if (!source_id || strcmp(source_id, "browser_source") != 0) {
return false;
}

// Get the source settings data
obs_data_t *settings = obs_source_get_settings(source);
if (!settings) {
return false;
}

// Extract browser source specific settings from the data
const char *url = obs_data_get_string(settings, "url");
int width = (int)obs_data_get_int(settings, "width");
int height = (int)obs_data_get_int(settings, "height");
bool shutdown = obs_data_get_bool(settings, "shutdown");
int webpage_control_level = (int)obs_data_get_int(settings, "webpage_control_level");

json = {{"inputKind", "browser_source"},
{"inputSettings",
{{"height", height},
{"shutdown", shutdown},
{"url", url ? url : ""},
{"webpage_control_level", webpage_control_level},
{"width", width}}}};

// Release allocated memory
obs_data_release(settings);
} else if (name == "getSceneItemTransform") {
const std::string scene_name = input_args->GetString(1).ToString();
int64_t scene_item_id = input_args->GetInt(2);

OBSSourceAutoRelease source = obs_get_source_by_name(scene_name.c_str());
if (!source) {
return false;
}

// Verify that the source is actually a scene
if (!obs_source_is_scene(source)) {
return false;
}

// Convert source to scene object for item lookup
obs_scene_t *scene = obs_scene_from_source(source);
if (!scene) {
return false;
}

// Find the specific scene item by its unique ID
obs_sceneitem_t *item = obs_scene_find_sceneitem_by_id(scene, scene_item_id);
if (!item) {
return false;
}

// Retrieve transform and crop information from the scene item
struct obs_transform_info transform;
struct obs_sceneitem_crop crop;
obs_sceneitem_get_info2(item, &transform);
obs_sceneitem_get_crop(item, &crop);

json = {{"position", {{"x", transform.pos.x}, {"y", transform.pos.y}}},
{"rotation", transform.rot},
{"scale", {{"x", transform.scale.x}, {"y", transform.scale.y}}},
{"alignment", transform.alignment},
{"boundsType", transform.bounds_type},
{"boundsAlignment", transform.bounds_alignment},
{"bounds", {{"x", transform.bounds.x}, {"y", transform.bounds.y}}},
{"crop",
{{"left", crop.left},
{"top", crop.top},
{"right", crop.right},
{"bottom", crop.bottom}}}};
} else if (name == "getTransitions") {
struct obs_frontend_source_list list = {};
obs_frontend_get_transitions(&list);
Expand Down