diff --git a/CMakeLists.txt b/CMakeLists.txt
index f65f0ea0..86eead93 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -123,6 +123,13 @@ set(obs-websocket_SOURCES
src/utils/Crypto.cpp
src/utils/Json.cpp
src/utils/Obs.cpp
+ src/utils/Obs_StringHelper.cpp
+ src/utils/Obs_EnumHelper.cpp
+ src/utils/Obs_NumberHelper.cpp
+ src/utils/Obs_ListHelper.cpp
+ src/utils/Obs_DataHelper.cpp
+ src/utils/Obs_SearchHelper.cpp
+ src/utils/Obs_ActionHelper.cpp
src/utils/ObsVolumeMeter.cpp
src/utils/Platform.cpp
src/utils/Compat.cpp
diff --git a/src/utils/Obs.cpp b/src/utils/Obs.cpp
index 3b11ebe9..131e8d22 100644
--- a/src/utils/Obs.cpp
+++ b/src/utils/Obs.cpp
@@ -17,562 +17,5 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see
*/
-#include
-#include
-#include
-#include
-#include
-#include
-
#include "Obs.h"
-#include "../obs-websocket.h"
#include "../plugin-macros.generated.h"
-
-#define CASE(x) case x: return #x;
-
-#define RET_COMPARE(str, x) if (str == #x) return x;
-
-std::vector ConvertStringArray(char **array)
-{
- std::vector ret;
- if (!array)
- return ret;
-
- size_t index = 0;
- char* value = nullptr;
- do {
- value = array[index];
- if (value)
- ret.push_back(value);
- index++;
- } while (value);
-
- return ret;
-}
-
-std::string Utils::Obs::StringHelper::GetObsVersion()
-{
- uint32_t version = obs_get_version();
-
- uint8_t major, minor, patch;
- major = (version >> 24) & 0xFF;
- minor = (version >> 16) & 0xFF;
- patch = version & 0xFF;
-
- QString combined = QString("%1.%2.%3").arg(major).arg(minor).arg(patch);
- return combined.toStdString();
-}
-
-std::string Utils::Obs::StringHelper::GetCurrentSceneCollection()
-{
- char *sceneCollectionName = obs_frontend_get_current_scene_collection();
- std::string ret = sceneCollectionName;
- bfree(sceneCollectionName);
- return ret;
-}
-
-std::string Utils::Obs::StringHelper::GetCurrentProfile()
-{
- char *profileName = obs_frontend_get_current_profile();
- std::string ret = profileName;
- bfree(profileName);
- return ret;
-}
-
-std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
-{
- char *profilePath = obs_frontend_get_current_profile_path();
- std::string ret = profilePath;
- bfree(profilePath);
- return ret;
-}
-
-std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
-{
- //char *recordOutputPath = obs_frontend_get_current_record_output_path();
- //std::string ret = recordOutputPath;
- //bfree(recordOutputPath);
- //return ret;
-
- return "";
-}
-
-std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
-{
- obs_source_type sourceType = obs_source_get_type(source);
-
- switch (sourceType) {
- default:
- CASE(OBS_SOURCE_TYPE_INPUT)
- CASE(OBS_SOURCE_TYPE_FILTER)
- CASE(OBS_SOURCE_TYPE_TRANSITION)
- CASE(OBS_SOURCE_TYPE_SCENE)
- }
-}
-
-std::string Utils::Obs::StringHelper::GetInputMonitorType(obs_source_t *input)
-{
- obs_monitoring_type monitorType = obs_source_get_monitoring_type(input);
-
- switch (monitorType) {
- default:
- CASE(OBS_MONITORING_TYPE_NONE)
- CASE(OBS_MONITORING_TYPE_MONITOR_ONLY)
- CASE(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
- }
-}
-
-std::string Utils::Obs::StringHelper::GetMediaInputState(obs_source_t *input)
-{
- obs_media_state mediaState = obs_source_media_get_state(input);
-
- switch (mediaState) {
- default:
- CASE(OBS_MEDIA_STATE_NONE)
- CASE(OBS_MEDIA_STATE_PLAYING)
- CASE(OBS_MEDIA_STATE_OPENING)
- CASE(OBS_MEDIA_STATE_BUFFERING)
- CASE(OBS_MEDIA_STATE_PAUSED)
- CASE(OBS_MEDIA_STATE_STOPPED)
- CASE(OBS_MEDIA_STATE_ENDED)
- CASE(OBS_MEDIA_STATE_ERROR)
- }
-}
-
-std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
-{
- OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
- calldata_t cd = {0};
- proc_handler_t *ph = obs_output_get_proc_handler(output);
- proc_handler_call(ph, "get_last_replay", &cd);
- auto ret = calldata_string(&cd, "path");
- calldata_free(&cd);
- return ret;
-}
-
-std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
-{
- switch (type) {
- default:
- CASE(OBS_BOUNDS_NONE)
- CASE(OBS_BOUNDS_STRETCH)
- CASE(OBS_BOUNDS_SCALE_INNER)
- CASE(OBS_BOUNDS_SCALE_OUTER)
- CASE(OBS_BOUNDS_SCALE_TO_WIDTH)
- CASE(OBS_BOUNDS_SCALE_TO_HEIGHT)
- CASE(OBS_BOUNDS_MAX_ONLY)
- }
-}
-
-std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
-{
- uint64_t secs = ms / 1000ULL;
- uint64_t minutes = secs / 60ULL;
-
- uint64_t hoursPart = minutes / 60ULL;
- uint64_t minutesPart = minutes % 60ULL;
- uint64_t secsPart = secs % 60ULL;
- uint64_t msPart = ms % 1000ULL;
-
- QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
- return formatted.toStdString();
-}
-
-enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
-{
- RET_COMPARE(boundsType, OBS_BOUNDS_NONE);
- RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH);
- RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_INNER);
- RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_OUTER);
- RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_WIDTH);
- RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_HEIGHT);
- RET_COMPARE(boundsType, OBS_BOUNDS_MAX_ONLY);
-
- return OBS_BOUNDS_NONE;
-}
-
-enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
-{
- RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY);
- RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE);
- RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP);
- RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART);
- RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT);
- RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
-
- return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
-}
-
-uint64_t Utils::Obs::NumberHelper::GetOutputDuration(obs_output_t *output)
-{
- if (!output || !obs_output_active(output))
- return 0;
-
- video_t* video = obs_output_video(output);
- uint64_t frameTimeNs = video_output_get_frame_time(video);
- int totalFrames = obs_output_get_total_frames(output);
-
- return util_mul_div64(totalFrames, frameTimeNs, 1000000ULL);
-}
-
-size_t Utils::Obs::NumberHelper::GetSceneCount()
-{
- size_t ret;
- auto sceneEnumProc = [](void *param, obs_source_t *scene) {
- auto ret = reinterpret_cast(param);
-
- if (obs_source_is_group(scene))
- return true;
-
- (*ret)++;
- return true;
- };
-
- obs_enum_scenes(sceneEnumProc, &ret);
-
- return ret;
-}
-
-std::vector Utils::Obs::ListHelper::GetSceneCollectionList()
-{
- char** sceneCollections = obs_frontend_get_scene_collections();
- auto ret = ConvertStringArray(sceneCollections);
- bfree(sceneCollections);
- return ret;
-}
-
-std::vector Utils::Obs::ListHelper::GetProfileList()
-{
- char** profiles = obs_frontend_get_profiles();
- auto ret = ConvertStringArray(profiles);
- bfree(profiles);
- return ret;
-}
-
-std::vector Utils::Obs::ListHelper::GetHotkeyList()
-{
- std::vector ret;
-
- obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) {
- auto ret = reinterpret_cast *>(data);
-
- ret->push_back(hotkey);
-
- return true;
- }, &ret);
-
- return ret;
-}
-
-std::vector Utils::Obs::ListHelper::GetHotkeyNameList()
-{
- auto hotkeys = GetHotkeyList();
-
- std::vector ret;
- for (auto hotkey : hotkeys) {
- ret.emplace_back(obs_hotkey_get_name(hotkey));
- }
-
- return ret;
-}
-
-std::vector Utils::Obs::ListHelper::GetSceneList()
-{
- obs_frontend_source_list sceneList = {};
- obs_frontend_get_scenes(&sceneList);
-
- std::vector ret;
- for (size_t i = 0; i < sceneList.sources.num; i++) {
- obs_source_t *scene = sceneList.sources.array[i];
-
- if (obs_source_is_group(scene))
- continue;
-
- json sceneJson;
- sceneJson["sceneName"] = obs_source_get_name(scene);
- sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
-
- ret.push_back(sceneJson);
- }
-
- obs_frontend_source_list_free(&sceneList);
-
- // Reverse the vector order to match other array returns
- std::reverse(ret.begin(), ret.end());
-
- return ret;
-}
-
-std::vector Utils::Obs::ListHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
-{
- std::pair, bool> enumData;
- enumData.second = basic;
-
- obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
- auto enumData = reinterpret_cast, bool>*>(param);
-
- json item;
- item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
- // Should be slightly faster than calling obs_sceneitem_get_order_position()
- item["sceneItemIndex"] = enumData->first.size();
- if (!enumData->second) {
- OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
- item["sourceName"] = obs_source_get_name(itemSource);
- item["sourceType"] = StringHelper::GetSourceType(itemSource);
- if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
- item["inputKind"] = obs_source_get_id(itemSource);
- else
- item["inputKind"] = nullptr;
- if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_SCENE)
- item["isGroup"] = obs_source_is_group(itemSource);
- else
- item["isGroup"] = nullptr;
- }
-
- enumData->first.push_back(item);
-
- return true;
- }, &enumData);
-
- return enumData.first;
-}
-
-std::vector Utils::Obs::ListHelper::GetTransitionList()
-{
- obs_frontend_source_list transitionList = {};
- obs_frontend_get_transitions(&transitionList);
-
- std::vector ret;
- for (size_t i = 0; i < transitionList.sources.num; i++) {
- obs_source_t *transition = transitionList.sources.array[i];
- json transitionJson;
- transitionJson["transitionName"] = obs_source_get_name(transition);
- transitionJson["transitionKind"] = obs_source_get_id(transition);
- transitionJson["transitionFixed"] = obs_transition_fixed(transition);
- ret.push_back(transitionJson);
- }
-
- obs_frontend_source_list_free(&transitionList);
-
- return ret;
-}
-
-struct EnumInputInfo {
- std::string inputKind; // For searching by input kind
- std::vector inputs;
-};
-
-std::vector Utils::Obs::ListHelper::GetInputList(std::string inputKind)
-{
- EnumInputInfo inputInfo;
- inputInfo.inputKind = inputKind;
-
- auto inputEnumProc = [](void *param, obs_source_t *input) {
- // Sanity check in case the API changes
- if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
- return true;
-
- auto inputInfo = reinterpret_cast(param);
-
- std::string inputKind = obs_source_get_id(input);
-
- if (!inputInfo->inputKind.empty() && inputInfo->inputKind != inputKind)
- return true;
-
- json inputJson;
- inputJson["inputName"] = obs_source_get_name(input);
- inputJson["inputKind"] = inputKind;
- inputJson["unversionedInputKind"] = obs_source_get_unversioned_id(input);
-
- inputInfo->inputs.push_back(inputJson);
- return true;
- };
- // Actually enumerates only public inputs, despite the name
- obs_enum_sources(inputEnumProc, &inputInfo);
-
- return inputInfo.inputs;
-}
-
-std::vector Utils::Obs::ListHelper::GetInputKindList(bool unversioned, bool includeDisabled)
-{
- std::vector ret;
-
- size_t idx = 0;
- const char *kind;
- const char *unversioned_kind;
- while (obs_enum_input_types2(idx++, &kind, &unversioned_kind)) {
- uint32_t caps = obs_get_source_output_flags(kind);
-
- if (!includeDisabled && (caps & OBS_SOURCE_CAP_DISABLED) != 0)
- continue;
-
- if (unversioned)
- ret.push_back(unversioned_kind);
- else
- ret.push_back(kind);
- }
-
- return ret;
-}
-
-json Utils::Obs::DataHelper::GetStats()
-{
- json ret;
-
- config_t* currentProfile = obs_frontend_get_profile_config();
- const char* outputMode = config_get_string(currentProfile, "Output", "Mode");
- const char* recordPath = strcmp(outputMode, "Advanced") ? config_get_string(currentProfile, "SimpleOutput", "FilePath") : config_get_string(currentProfile, "AdvOut", "RecFilePath");
-
- video_t* video = obs_get_video();
-
- ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo());
- ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0);
- ret["availableDiskSpace"] = (double)os_get_free_disk_space(recordPath) / (1024.0 * 1024.0);
- ret["activeFps"] = obs_get_active_fps();
- ret["averageFrameRenderTime"] = (double)obs_get_average_frame_time_ns() / 1000000.0;
- ret["renderSkippedFrames"] = obs_get_lagged_frames();
- ret["renderTotalFrames"] = obs_get_total_frames();
- ret["outputSkippedFrames"] = video_output_get_skipped_frames(video);
- ret["outputTotalFrames"] = video_output_get_total_frames(video);
-
- return ret;
-}
-
-json Utils::Obs::DataHelper::GetSceneItemTransform(obs_sceneitem_t *item)
-{
- json ret;
-
- obs_transform_info osi;
- obs_sceneitem_crop crop;
- obs_sceneitem_get_info(item, &osi);
- obs_sceneitem_get_crop(item, &crop);
-
- OBSSource source = obs_sceneitem_get_source(item);
- float sourceWidth = float(obs_source_get_width(source));
- float sourceHeight = float(obs_source_get_height(source));
-
- ret["sourceWidth"] = sourceWidth;
- ret["sourceHeight"] = sourceHeight;
-
- ret["positionX"] = osi.pos.x;
- ret["positionY"] = osi.pos.y;
-
- ret["rotation"] = osi.rot;
-
- ret["scaleX"] = osi.scale.x;
- ret["scaleY"] = osi.scale.y;
-
- ret["width"] = osi.scale.x * sourceWidth;
- ret["height"] = osi.scale.y * sourceHeight;
-
- ret["alignment"] = osi.alignment;
-
- ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
- ret["boundsAlignment"] = osi.bounds_alignment;
- ret["boundsWidth"] = osi.bounds.x;
- ret["boundsHeight"] = osi.bounds.y;
-
- ret["cropLeft"] = int(crop.left);
- ret["cropRight"] = int(crop.right);
- ret["cropTop"] = int(crop.top);
- ret["cropBottom"] = int(crop.bottom);
-
- return ret;
-}
-
-obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
-{
- if (name.empty())
- return nullptr;
-
- auto hotkeys = ListHelper::GetHotkeyList();
-
- for (auto hotkey : hotkeys) {
- if (obs_hotkey_get_name(hotkey) == name)
- return hotkey;
- }
-
- return nullptr;
-}
-
-// Increments item ref. Use OBSSceneItemAutoRelease
-obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name)
-{
- if (name.empty())
- return nullptr;
-
- // Finds first matching scene item in scene, search starts at index 0
- OBSSceneItem ret = obs_scene_find_source(scene, name.c_str());
- obs_sceneitem_addref(ret);
-
- return ret;
-}
-
-struct CreateSceneItemData {
- obs_source_t *source; // In
- bool sceneItemEnabled; // In
- obs_transform_info *sceneItemTransform = nullptr; // In
- obs_sceneitem_crop *sceneItemCrop = nullptr; // In
- OBSSceneItem sceneItem; // Out
-};
-
-void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
-{
- auto *data = reinterpret_cast(_data);
- data->sceneItem = obs_scene_add(scene, data->source);
-
- if (data->sceneItemTransform)
- obs_sceneitem_set_info(data->sceneItem, data->sceneItemTransform);
-
- if (data->sceneItemCrop)
- obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);
-
- obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
-}
-
-obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled, obs_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
-{
- // Sanity check for valid scene
- if (!(source && scene))
- return nullptr;
-
- // Create data struct and populate for scene item creation
- CreateSceneItemData data;
- data.source = source;
- data.sceneItemEnabled = sceneItemEnabled;
- data.sceneItemTransform = sceneItemTransform;
- data.sceneItemCrop = sceneItemCrop;
-
- // Enter graphics context and create the scene item
- obs_enter_graphics();
- obs_scene_atomic_update(scene, CreateSceneItemHelper, &data);
- obs_leave_graphics();
-
- obs_sceneitem_addref(data.sceneItem);
-
- return data.sceneItem;
-}
-
-obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
-{
- // Create the input
- OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
-
- // Check that everything was created properly
- if (!input)
- return nullptr;
-
- // Apparently not all default input properties actually get applied on creation (smh)
- uint32_t flags = obs_source_get_output_flags(input);
- if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0)
- obs_source_set_monitoring_type(input, OBS_MONITORING_TYPE_MONITOR_ONLY);
-
- // Create a scene item for the input
- obs_sceneitem_t *ret = CreateSceneItem(input, scene, sceneItemEnabled);
-
- // If creation failed, remove the input
- if (!ret)
- obs_source_remove(input);
-
- return ret;
-}
diff --git a/src/utils/Obs.h b/src/utils/Obs.h
index f5beecad..e92788ce 100644
--- a/src/utils/Obs.h
+++ b/src/utils/Obs.h
@@ -21,6 +21,7 @@ with this program. If not, see
#include
#include
+#include
#include "Json.h"
diff --git a/src/utils/Obs_ActionHelper.cpp b/src/utils/Obs_ActionHelper.cpp
new file mode 100644
index 00000000..5442219b
--- /dev/null
+++ b/src/utils/Obs_ActionHelper.cpp
@@ -0,0 +1,89 @@
+/*
+obs-websocket
+Copyright (C) 2020-2021 Kyle Manning
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see
+*/
+
+#include "Obs.h"
+#include "../plugin-macros.generated.h"
+
+struct CreateSceneItemData {
+ obs_source_t *source; // In
+ bool sceneItemEnabled; // In
+ obs_transform_info *sceneItemTransform = nullptr; // In
+ obs_sceneitem_crop *sceneItemCrop = nullptr; // In
+ OBSSceneItem sceneItem; // Out
+};
+
+void CreateSceneItemHelper(void *_data, obs_scene_t *scene)
+{
+ auto *data = reinterpret_cast(_data);
+ data->sceneItem = obs_scene_add(scene, data->source);
+
+ if (data->sceneItemTransform)
+ obs_sceneitem_set_info(data->sceneItem, data->sceneItemTransform);
+
+ if (data->sceneItemCrop)
+ obs_sceneitem_set_crop(data->sceneItem, data->sceneItemCrop);
+
+ obs_sceneitem_set_visible(data->sceneItem, data->sceneItemEnabled);
+}
+
+obs_sceneitem_t *Utils::Obs::ActionHelper::CreateSceneItem(obs_source_t *source, obs_scene_t *scene, bool sceneItemEnabled, obs_transform_info *sceneItemTransform, obs_sceneitem_crop *sceneItemCrop)
+{
+ // Sanity check for valid scene
+ if (!(source && scene))
+ return nullptr;
+
+ // Create data struct and populate for scene item creation
+ CreateSceneItemData data;
+ data.source = source;
+ data.sceneItemEnabled = sceneItemEnabled;
+ data.sceneItemTransform = sceneItemTransform;
+ data.sceneItemCrop = sceneItemCrop;
+
+ // Enter graphics context and create the scene item
+ obs_enter_graphics();
+ obs_scene_atomic_update(scene, CreateSceneItemHelper, &data);
+ obs_leave_graphics();
+
+ obs_sceneitem_addref(data.sceneItem);
+
+ return data.sceneItem;
+}
+
+obs_sceneitem_t *Utils::Obs::ActionHelper::CreateInput(std::string inputName, std::string inputKind, obs_data_t *inputSettings, obs_scene_t *scene, bool sceneItemEnabled)
+{
+ // Create the input
+ OBSSourceAutoRelease input = obs_source_create(inputKind.c_str(), inputName.c_str(), inputSettings, nullptr);
+
+ // Check that everything was created properly
+ if (!input)
+ return nullptr;
+
+ // Apparently not all default input properties actually get applied on creation (smh)
+ uint32_t flags = obs_source_get_output_flags(input);
+ if ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0)
+ obs_source_set_monitoring_type(input, OBS_MONITORING_TYPE_MONITOR_ONLY);
+
+ // Create a scene item for the input
+ obs_sceneitem_t *ret = CreateSceneItem(input, scene, sceneItemEnabled);
+
+ // If creation failed, remove the input
+ if (!ret)
+ obs_source_remove(input);
+
+ return ret;
+}
diff --git a/src/utils/Obs_DataHelper.cpp b/src/utils/Obs_DataHelper.cpp
new file mode 100644
index 00000000..1d5b33e1
--- /dev/null
+++ b/src/utils/Obs_DataHelper.cpp
@@ -0,0 +1,89 @@
+/*
+obs-websocket
+Copyright (C) 2016-2021 Stephane Lepin
+Copyright (C) 2020-2021 Kyle Manning
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see
+*/
+
+#include
+
+#include "Obs.h"
+#include "../obs-websocket.h"
+#include "../plugin-macros.generated.h"
+
+json Utils::Obs::DataHelper::GetStats()
+{
+ json ret;
+
+ config_t* currentProfile = obs_frontend_get_profile_config();
+ const char* outputMode = config_get_string(currentProfile, "Output", "Mode");
+ const char* recordPath = strcmp(outputMode, "Advanced") ? config_get_string(currentProfile, "SimpleOutput", "FilePath") : config_get_string(currentProfile, "AdvOut", "RecFilePath");
+
+ video_t* video = obs_get_video();
+
+ ret["cpuUsage"] = os_cpu_usage_info_query(GetCpuUsageInfo());
+ ret["memoryUsage"] = (double)os_get_proc_resident_size() / (1024.0 * 1024.0);
+ ret["availableDiskSpace"] = (double)os_get_free_disk_space(recordPath) / (1024.0 * 1024.0);
+ ret["activeFps"] = obs_get_active_fps();
+ ret["averageFrameRenderTime"] = (double)obs_get_average_frame_time_ns() / 1000000.0;
+ ret["renderSkippedFrames"] = obs_get_lagged_frames();
+ ret["renderTotalFrames"] = obs_get_total_frames();
+ ret["outputSkippedFrames"] = video_output_get_skipped_frames(video);
+ ret["outputTotalFrames"] = video_output_get_total_frames(video);
+
+ return ret;
+}
+
+json Utils::Obs::DataHelper::GetSceneItemTransform(obs_sceneitem_t *item)
+{
+ json ret;
+
+ obs_transform_info osi;
+ obs_sceneitem_crop crop;
+ obs_sceneitem_get_info(item, &osi);
+ obs_sceneitem_get_crop(item, &crop);
+
+ OBSSource source = obs_sceneitem_get_source(item);
+ float sourceWidth = float(obs_source_get_width(source));
+ float sourceHeight = float(obs_source_get_height(source));
+
+ ret["sourceWidth"] = sourceWidth;
+ ret["sourceHeight"] = sourceHeight;
+
+ ret["positionX"] = osi.pos.x;
+ ret["positionY"] = osi.pos.y;
+
+ ret["rotation"] = osi.rot;
+
+ ret["scaleX"] = osi.scale.x;
+ ret["scaleY"] = osi.scale.y;
+
+ ret["width"] = osi.scale.x * sourceWidth;
+ ret["height"] = osi.scale.y * sourceHeight;
+
+ ret["alignment"] = osi.alignment;
+
+ ret["boundsType"] = StringHelper::GetSceneItemBoundsType(osi.bounds_type);
+ ret["boundsAlignment"] = osi.bounds_alignment;
+ ret["boundsWidth"] = osi.bounds.x;
+ ret["boundsHeight"] = osi.bounds.y;
+
+ ret["cropLeft"] = int(crop.left);
+ ret["cropRight"] = int(crop.right);
+ ret["cropTop"] = int(crop.top);
+ ret["cropBottom"] = int(crop.bottom);
+
+ return ret;
+}
diff --git a/src/utils/Obs_EnumHelper.cpp b/src/utils/Obs_EnumHelper.cpp
new file mode 100644
index 00000000..b0c21aa4
--- /dev/null
+++ b/src/utils/Obs_EnumHelper.cpp
@@ -0,0 +1,47 @@
+/*
+obs-websocket
+Copyright (C) 2020-2021 Kyle Manning
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see
+*/
+
+#include "Obs.h"
+#include "../plugin-macros.generated.h"
+
+#define RET_COMPARE(str, x) if (str == #x) return x;
+
+enum obs_bounds_type Utils::Obs::EnumHelper::GetSceneItemBoundsType(std::string boundsType)
+{
+ RET_COMPARE(boundsType, OBS_BOUNDS_NONE);
+ RET_COMPARE(boundsType, OBS_BOUNDS_STRETCH);
+ RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_INNER);
+ RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_OUTER);
+ RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_WIDTH);
+ RET_COMPARE(boundsType, OBS_BOUNDS_SCALE_TO_HEIGHT);
+ RET_COMPARE(boundsType, OBS_BOUNDS_MAX_ONLY);
+
+ return OBS_BOUNDS_NONE;
+}
+
+enum ObsMediaInputAction Utils::Obs::EnumHelper::GetMediaInputAction(std::string mediaAction)
+{
+ RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PLAY);
+ RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PAUSE);
+ RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_STOP);
+ RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_RESTART);
+ RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NEXT);
+ RET_COMPARE(mediaAction, OBS_WEBSOCKET_MEDIA_INPUT_ACTION_PREVIOUS);
+
+ return OBS_WEBSOCKET_MEDIA_INPUT_ACTION_NONE;
+}
diff --git a/src/utils/Obs_ListHelper.cpp b/src/utils/Obs_ListHelper.cpp
new file mode 100644
index 00000000..9a40fbd0
--- /dev/null
+++ b/src/utils/Obs_ListHelper.cpp
@@ -0,0 +1,222 @@
+/*
+obs-websocket
+Copyright (C) 2016-2021 Stephane Lepin
+Copyright (C) 2020-2021 Kyle Manning
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see
+*/
+
+#include
+
+#include "Obs.h"
+#include "../plugin-macros.generated.h"
+
+static std::vector ConvertStringArray(char **array)
+{
+ std::vector ret;
+ if (!array)
+ return ret;
+
+ size_t index = 0;
+ char* value = nullptr;
+ do {
+ value = array[index];
+ if (value)
+ ret.push_back(value);
+ index++;
+ } while (value);
+
+ return ret;
+}
+
+std::vector Utils::Obs::ListHelper::GetSceneCollectionList()
+{
+ char** sceneCollections = obs_frontend_get_scene_collections();
+ auto ret = ConvertStringArray(sceneCollections);
+ bfree(sceneCollections);
+ return ret;
+}
+
+std::vector Utils::Obs::ListHelper::GetProfileList()
+{
+ char** profiles = obs_frontend_get_profiles();
+ auto ret = ConvertStringArray(profiles);
+ bfree(profiles);
+ return ret;
+}
+
+std::vector Utils::Obs::ListHelper::GetHotkeyList()
+{
+ std::vector ret;
+
+ obs_enum_hotkeys([](void* data, obs_hotkey_id, obs_hotkey_t* hotkey) {
+ auto ret = reinterpret_cast *>(data);
+
+ ret->push_back(hotkey);
+
+ return true;
+ }, &ret);
+
+ return ret;
+}
+
+std::vector Utils::Obs::ListHelper::GetHotkeyNameList()
+{
+ auto hotkeys = GetHotkeyList();
+
+ std::vector ret;
+ for (auto hotkey : hotkeys)
+ ret.emplace_back(obs_hotkey_get_name(hotkey));
+
+ return ret;
+}
+
+std::vector Utils::Obs::ListHelper::GetSceneList()
+{
+ obs_frontend_source_list sceneList = {};
+ obs_frontend_get_scenes(&sceneList);
+
+ std::vector ret;
+ for (size_t i = 0; i < sceneList.sources.num; i++) {
+ obs_source_t *scene = sceneList.sources.array[i];
+
+ if (obs_source_is_group(scene))
+ continue;
+
+ json sceneJson;
+ sceneJson["sceneName"] = obs_source_get_name(scene);
+ sceneJson["sceneIndex"] = sceneList.sources.num - i - 1;
+
+ ret.push_back(sceneJson);
+ }
+
+ obs_frontend_source_list_free(&sceneList);
+
+ // Reverse the vector order to match other array returns
+ std::reverse(ret.begin(), ret.end());
+
+ return ret;
+}
+
+std::vector Utils::Obs::ListHelper::GetSceneItemList(obs_scene_t *scene, bool basic)
+{
+ std::pair, bool> enumData;
+ enumData.second = basic;
+
+ obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) {
+ auto enumData = reinterpret_cast, bool>*>(param);
+
+ json item;
+ item["sceneItemId"] = obs_sceneitem_get_id(sceneItem);
+ // Should be slightly faster than calling obs_sceneitem_get_order_position()
+ item["sceneItemIndex"] = enumData->first.size();
+ if (!enumData->second) {
+ OBSSource itemSource = obs_sceneitem_get_source(sceneItem);
+ item["sourceName"] = obs_source_get_name(itemSource);
+ item["sourceType"] = StringHelper::GetSourceType(itemSource);
+ if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_INPUT)
+ item["inputKind"] = obs_source_get_id(itemSource);
+ else
+ item["inputKind"] = nullptr;
+ if (obs_source_get_type(itemSource) == OBS_SOURCE_TYPE_SCENE)
+ item["isGroup"] = obs_source_is_group(itemSource);
+ else
+ item["isGroup"] = nullptr;
+ }
+
+ enumData->first.push_back(item);
+
+ return true;
+ }, &enumData);
+
+ return enumData.first;
+}
+
+std::vector Utils::Obs::ListHelper::GetTransitionList()
+{
+ obs_frontend_source_list transitionList = {};
+ obs_frontend_get_transitions(&transitionList);
+
+ std::vector ret;
+ for (size_t i = 0; i < transitionList.sources.num; i++) {
+ obs_source_t *transition = transitionList.sources.array[i];
+ json transitionJson;
+ transitionJson["transitionName"] = obs_source_get_name(transition);
+ transitionJson["transitionKind"] = obs_source_get_id(transition);
+ transitionJson["transitionFixed"] = obs_transition_fixed(transition);
+ ret.push_back(transitionJson);
+ }
+
+ obs_frontend_source_list_free(&transitionList);
+
+ return ret;
+}
+
+struct EnumInputInfo {
+ std::string inputKind; // For searching by input kind
+ std::vector inputs;
+};
+
+std::vector Utils::Obs::ListHelper::GetInputList(std::string inputKind)
+{
+ EnumInputInfo inputInfo;
+ inputInfo.inputKind = inputKind;
+
+ auto inputEnumProc = [](void *param, obs_source_t *input) {
+ // Sanity check in case the API changes
+ if (obs_source_get_type(input) != OBS_SOURCE_TYPE_INPUT)
+ return true;
+
+ auto inputInfo = reinterpret_cast(param);
+
+ std::string inputKind = obs_source_get_id(input);
+
+ if (!inputInfo->inputKind.empty() && inputInfo->inputKind != inputKind)
+ return true;
+
+ json inputJson;
+ inputJson["inputName"] = obs_source_get_name(input);
+ inputJson["inputKind"] = inputKind;
+ inputJson["unversionedInputKind"] = obs_source_get_unversioned_id(input);
+
+ inputInfo->inputs.push_back(inputJson);
+ return true;
+ };
+ // Actually enumerates only public inputs, despite the name
+ obs_enum_sources(inputEnumProc, &inputInfo);
+
+ return inputInfo.inputs;
+}
+
+std::vector Utils::Obs::ListHelper::GetInputKindList(bool unversioned, bool includeDisabled)
+{
+ std::vector ret;
+
+ size_t idx = 0;
+ const char *kind;
+ const char *unversioned_kind;
+ while (obs_enum_input_types2(idx++, &kind, &unversioned_kind)) {
+ uint32_t caps = obs_get_source_output_flags(kind);
+
+ if (!includeDisabled && (caps & OBS_SOURCE_CAP_DISABLED) != 0)
+ continue;
+
+ if (unversioned)
+ ret.push_back(unversioned_kind);
+ else
+ ret.push_back(kind);
+ }
+
+ return ret;
+}
diff --git a/src/utils/Obs_NumberHelper.cpp b/src/utils/Obs_NumberHelper.cpp
new file mode 100644
index 00000000..a3891231
--- /dev/null
+++ b/src/utils/Obs_NumberHelper.cpp
@@ -0,0 +1,54 @@
+/*
+obs-websocket
+Copyright (C) 2016-2021 Stephane Lepin
+Copyright (C) 2020-2021 Kyle Manning
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see
+*/
+
+#include
+#include
+
+#include "Obs.h"
+#include "../plugin-macros.generated.h"
+
+uint64_t Utils::Obs::NumberHelper::GetOutputDuration(obs_output_t *output)
+{
+ if (!output || !obs_output_active(output))
+ return 0;
+
+ video_t* video = obs_output_video(output);
+ uint64_t frameTimeNs = video_output_get_frame_time(video);
+ int totalFrames = obs_output_get_total_frames(output);
+
+ return util_mul_div64(totalFrames, frameTimeNs, 1000000ULL);
+}
+
+size_t Utils::Obs::NumberHelper::GetSceneCount()
+{
+ size_t ret;
+ auto sceneEnumProc = [](void *param, obs_source_t *scene) {
+ auto ret = reinterpret_cast(param);
+
+ if (obs_source_is_group(scene))
+ return true;
+
+ (*ret)++;
+ return true;
+ };
+
+ obs_enum_scenes(sceneEnumProc, &ret);
+
+ return ret;
+}
diff --git a/src/utils/Obs_SearchHelper.cpp b/src/utils/Obs_SearchHelper.cpp
new file mode 100644
index 00000000..3b3e95ff
--- /dev/null
+++ b/src/utils/Obs_SearchHelper.cpp
@@ -0,0 +1,48 @@
+/*
+obs-websocket
+Copyright (C) 2020-2021 Kyle Manning
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see
+*/
+
+#include "Obs.h"
+#include "../plugin-macros.generated.h"
+
+obs_hotkey_t *Utils::Obs::SearchHelper::GetHotkeyByName(std::string name)
+{
+ if (name.empty())
+ return nullptr;
+
+ auto hotkeys = ListHelper::GetHotkeyList();
+
+ for (auto hotkey : hotkeys) {
+ if (obs_hotkey_get_name(hotkey) == name)
+ return hotkey;
+ }
+
+ return nullptr;
+}
+
+// Increments item ref. Use OBSSceneItemAutoRelease
+obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name)
+{
+ if (name.empty())
+ return nullptr;
+
+ // Finds first matching scene item in scene, search starts at index 0
+ OBSSceneItem ret = obs_scene_find_source(scene, name.c_str());
+ obs_sceneitem_addref(ret);
+
+ return ret;
+}
diff --git a/src/utils/Obs_StringHelper.cpp b/src/utils/Obs_StringHelper.cpp
new file mode 100644
index 00000000..3abf6c50
--- /dev/null
+++ b/src/utils/Obs_StringHelper.cpp
@@ -0,0 +1,154 @@
+/*
+obs-websocket
+Copyright (C) 2016-2021 Stephane Lepin
+Copyright (C) 2020-2021 Kyle Manning
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program. If not, see
+*/
+
+#include
+#include
+
+#include "Obs.h"
+#include "../plugin-macros.generated.h"
+
+#define CASE(x) case x: return #x;
+
+std::string Utils::Obs::StringHelper::GetObsVersion()
+{
+ uint32_t version = obs_get_version();
+
+ uint8_t major, minor, patch;
+ major = (version >> 24) & 0xFF;
+ minor = (version >> 16) & 0xFF;
+ patch = version & 0xFF;
+
+ QString combined = QString("%1.%2.%3").arg(major).arg(minor).arg(patch);
+ return combined.toStdString();
+}
+
+std::string Utils::Obs::StringHelper::GetCurrentSceneCollection()
+{
+ char *sceneCollectionName = obs_frontend_get_current_scene_collection();
+ std::string ret = sceneCollectionName;
+ bfree(sceneCollectionName);
+ return ret;
+}
+
+std::string Utils::Obs::StringHelper::GetCurrentProfile()
+{
+ char *profileName = obs_frontend_get_current_profile();
+ std::string ret = profileName;
+ bfree(profileName);
+ return ret;
+}
+
+std::string Utils::Obs::StringHelper::GetCurrentProfilePath()
+{
+ char *profilePath = obs_frontend_get_current_profile_path();
+ std::string ret = profilePath;
+ bfree(profilePath);
+ return ret;
+}
+
+std::string Utils::Obs::StringHelper::GetCurrentRecordOutputPath()
+{
+ //char *recordOutputPath = obs_frontend_get_current_record_output_path();
+ //std::string ret = recordOutputPath;
+ //bfree(recordOutputPath);
+ //return ret;
+
+ return "";
+}
+
+std::string Utils::Obs::StringHelper::GetSourceType(obs_source_t *source)
+{
+ obs_source_type sourceType = obs_source_get_type(source);
+
+ switch (sourceType) {
+ default:
+ CASE(OBS_SOURCE_TYPE_INPUT)
+ CASE(OBS_SOURCE_TYPE_FILTER)
+ CASE(OBS_SOURCE_TYPE_TRANSITION)
+ CASE(OBS_SOURCE_TYPE_SCENE)
+ }
+}
+
+std::string Utils::Obs::StringHelper::GetInputMonitorType(obs_source_t *input)
+{
+ obs_monitoring_type monitorType = obs_source_get_monitoring_type(input);
+
+ switch (monitorType) {
+ default:
+ CASE(OBS_MONITORING_TYPE_NONE)
+ CASE(OBS_MONITORING_TYPE_MONITOR_ONLY)
+ CASE(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT)
+ }
+}
+
+std::string Utils::Obs::StringHelper::GetMediaInputState(obs_source_t *input)
+{
+ obs_media_state mediaState = obs_source_media_get_state(input);
+
+ switch (mediaState) {
+ default:
+ CASE(OBS_MEDIA_STATE_NONE)
+ CASE(OBS_MEDIA_STATE_PLAYING)
+ CASE(OBS_MEDIA_STATE_OPENING)
+ CASE(OBS_MEDIA_STATE_BUFFERING)
+ CASE(OBS_MEDIA_STATE_PAUSED)
+ CASE(OBS_MEDIA_STATE_STOPPED)
+ CASE(OBS_MEDIA_STATE_ENDED)
+ CASE(OBS_MEDIA_STATE_ERROR)
+ }
+}
+
+std::string Utils::Obs::StringHelper::GetLastReplayBufferFilePath()
+{
+ OBSOutputAutoRelease output = obs_frontend_get_replay_buffer_output();
+ calldata_t cd = {0};
+ proc_handler_t *ph = obs_output_get_proc_handler(output);
+ proc_handler_call(ph, "get_last_replay", &cd);
+ auto ret = calldata_string(&cd, "path");
+ calldata_free(&cd);
+ return ret;
+}
+
+std::string Utils::Obs::StringHelper::GetSceneItemBoundsType(enum obs_bounds_type type)
+{
+ switch (type) {
+ default:
+ CASE(OBS_BOUNDS_NONE)
+ CASE(OBS_BOUNDS_STRETCH)
+ CASE(OBS_BOUNDS_SCALE_INNER)
+ CASE(OBS_BOUNDS_SCALE_OUTER)
+ CASE(OBS_BOUNDS_SCALE_TO_WIDTH)
+ CASE(OBS_BOUNDS_SCALE_TO_HEIGHT)
+ CASE(OBS_BOUNDS_MAX_ONLY)
+ }
+}
+
+std::string Utils::Obs::StringHelper::DurationToTimecode(uint64_t ms)
+{
+ uint64_t secs = ms / 1000ULL;
+ uint64_t minutes = secs / 60ULL;
+
+ uint64_t hoursPart = minutes / 60ULL;
+ uint64_t minutesPart = minutes % 60ULL;
+ uint64_t secsPart = secs % 60ULL;
+ uint64_t msPart = ms % 1000ULL;
+
+ QString formatted = QString::asprintf("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64, hoursPart, minutesPart, secsPart, msPart);
+ return formatted.toStdString();
+}