/* 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 "RequestHandler.h" /** * Gets an array of all inputs in OBS. * * @requestField ?inputKind | String | Restrict the array to only inputs of the specified kind | All kinds included * * @responseField inputs | Array | Array of inputs * * @requestType GetInputList * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputList(const Request &request) { std::string inputKind; if (request.Contains("inputKind")) { RequestStatus::RequestStatus statusCode; std::string comment; if (!request.ValidateOptionalString("inputKind", statusCode, comment)) return RequestResult::Error(statusCode, comment); inputKind = request.RequestData["inputKind"]; } json responseData; responseData["inputs"] = Utils::Obs::ArrayHelper::GetInputList(inputKind); return RequestResult::Success(responseData); } /** * Gets an array of all available input kinds in OBS. * * @requestField ?unversioned | Boolean | True == Return all kinds as unversioned, False == Return with version suffixes (if available) | false * * @responseField inputKinds | Array | Array of input kinds * * @requestType GetInputKindList * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputKindList(const Request &request) { bool unversioned = false; if (request.Contains("unversioned")) { RequestStatus::RequestStatus statusCode; std::string comment; if (!request.ValidateOptionalBoolean("unversioned", statusCode, comment)) return RequestResult::Error(statusCode, comment); unversioned = request.RequestData["unversioned"]; } json responseData; responseData["inputKinds"] = Utils::Obs::ArrayHelper::GetInputKindList(unversioned); return RequestResult::Success(responseData); } /** * Gets the names of all special inputs. * * @responseField desktop1 | String | Name of the Desktop Audio input * @responseField desktop2 | String | Name of the Desktop Audio 2 input * @responseField mic1 | String | Name of the Mic/Auxiliary Audio input * @responseField mic2 | String | Name of the Mic/Auxiliary Audio 2 input * @responseField mic3 | String | Name of the Mic/Auxiliary Audio 3 input * @responseField mic4 | String | Name of the Mic/Auxiliary Audio 4 input * * @requestType GetSpecialInputs * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetSpecialInputs(const Request &) { json responseData; std::vector channels = {"desktop1", "desktop2", "mic1", "mic2", "mic3", "mic4"}; uint32_t channelId = 1; for (auto &channel : channels) { OBSSourceAutoRelease input = obs_get_output_source(channelId); if (!input) responseData[channel] = nullptr; else responseData[channel] = obs_source_get_name(input); channelId++; } return RequestResult::Success(responseData); } /** * Creates a new input, adding it as a scene item to the specified scene. * * @requestField sceneName | String | Name of the scene to add the input to as a scene item * @requestField inputName | String | Name of the new input to created * @requestField inputKind | String | The kind of input to be created * @requestField ?inputSettings | Object | Settings object to initialize the input with | Default settings used * @requestField ?sceneItemEnabled | Boolean | Whether to set the created scene item to enabled or disabled | True * * @responseField sceneItemId | Number | ID of the newly created scene item * * @requestType CreateInput * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::CreateInput(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease sceneSource = request.ValidateScene("sceneName", statusCode, comment); if (!(sceneSource && request.ValidateString("inputName", statusCode, comment) && request.ValidateString("inputKind", statusCode, comment))) return RequestResult::Error(statusCode, comment); std::string inputName = request.RequestData["inputName"]; OBSSourceAutoRelease existingInput = obs_get_source_by_name(inputName.c_str()); if (existingInput) return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that input name."); std::string inputKind = request.RequestData["inputKind"]; auto kinds = Utils::Obs::ArrayHelper::GetInputKindList(); if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end()) return RequestResult::Error( RequestStatus::InvalidInputKind, "Your specified input kind is not supported by OBS. Check that your specified kind is properly versioned and that any necessary plugins are loaded."); OBSDataAutoRelease inputSettings = nullptr; if (request.Contains("inputSettings")) { if (!request.ValidateOptionalObject("inputSettings", statusCode, comment, true)) return RequestResult::Error(statusCode, comment); inputSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]); } OBSScene scene = obs_scene_from_source(sceneSource); bool sceneItemEnabled = true; if (request.Contains("sceneItemEnabled")) { if (!request.ValidateOptionalBoolean("sceneItemEnabled", statusCode, comment)) return RequestResult::Error(statusCode, comment); sceneItemEnabled = request.RequestData["sceneItemEnabled"]; } // Create the input and add it as a scene item to the destination scene OBSSceneItemAutoRelease sceneItem = Utils::Obs::ActionHelper::CreateInput(inputName, inputKind, inputSettings, scene, sceneItemEnabled); if (!sceneItem) return RequestResult::Error(RequestStatus::ResourceCreationFailed, "Creation of the input or scene item failed."); json responseData; responseData["sceneItemId"] = obs_sceneitem_get_id(sceneItem); return RequestResult::Success(responseData); } /** * Removes an existing input. * * Note: Will immediately remove all associated scene items. * * @requestField inputName | String | Name of the input to remove * * @requestType RemoveInput * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::RemoveInput(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); // Some implementations of removing sources release before remove, and some release after. // Releasing afterwards guarantees that we don't accidentally destroy the source before // remove if we happen to hold the last ref (very, very rare) obs_source_remove(input); return RequestResult::Success(); } /** * Sets the name of an input (rename). * * @requestField inputName | String | Current input name * @requestField newInputName | String | New name for the input * * @requestType SetInputName * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputName(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("newInputName", statusCode, comment))) return RequestResult::Error(statusCode, comment); std::string newInputName = request.RequestData["newInputName"]; OBSSourceAutoRelease existingSource = obs_get_source_by_name(newInputName.c_str()); if (existingSource) return RequestResult::Error(RequestStatus::ResourceAlreadyExists, "A source already exists by that new input name."); obs_source_set_name(input, newInputName.c_str()); return RequestResult::Success(); } /** * Gets the default settings for an input kind. * * @requestField inputKind | String | Input kind to get the default settings for * * @responseField defaultInputSettings | Object | Object of default settings for the input kind * * @requestType GetInputDefaultSettings * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputDefaultSettings(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; if (!request.ValidateString("inputKind", statusCode, comment)) return RequestResult::Error(statusCode, comment); std::string inputKind = request.RequestData["inputKind"]; auto kinds = Utils::Obs::ArrayHelper::GetInputKindList(); if (std::find(kinds.begin(), kinds.end(), inputKind) == kinds.end()) return RequestResult::Error(RequestStatus::InvalidInputKind); OBSDataAutoRelease defaultSettings = obs_get_source_defaults(inputKind.c_str()); if (!defaultSettings) return RequestResult::Error(RequestStatus::InvalidInputKind); json responseData; responseData["defaultInputSettings"] = Utils::Json::ObsDataToJson(defaultSettings, true); return RequestResult::Success(responseData); } /** * Gets the settings of an input. * * Note: Does not include defaults. To create the entire settings object, overlay `inputSettings` over the `defaultInputSettings` provided by `GetInputDefaultSettings`. * * @requestField inputName | String | Name of the input to get the settings of * * @responseField inputSettings | Object | Object of settings for the input * @responseField inputKind | String | The kind of the input * * @requestType GetInputSettings * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputSettings(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); OBSDataAutoRelease inputSettings = obs_source_get_settings(input); json responseData; responseData["inputSettings"] = Utils::Json::ObsDataToJson(inputSettings); responseData["inputKind"] = obs_source_get_id(input); return RequestResult::Success(responseData); } /** * Sets the settings of an input. * * @requestField inputName | String | Name of the input to set the settings of * @requestField inputSettings | Object | Object of settings to apply * @requestField ?overlay | Boolean | True == apply the settings on top of existing ones, False == reset the input to its defaults, then apply settings. | true * * @requestType SetInputSettings * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputSettings(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateObject("inputSettings", statusCode, comment, true))) return RequestResult::Error(statusCode, comment); bool overlay = true; if (request.Contains("overlay")) { if (!request.ValidateOptionalBoolean("overlay", statusCode, comment)) return RequestResult::Error(statusCode, comment); overlay = request.RequestData["overlay"]; } // Get the new settings and convert it to obs_data_t* OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["inputSettings"]); if (!newSettings) // This should never happen return RequestResult::Error(RequestStatus::RequestProcessingFailed, "An internal data conversion operation failed. Please report this!"); if (overlay) // Applies the new settings on top of the existing user settings obs_source_update(input, newSettings); else // Clears all user settings (leaving defaults) then applies the new settings obs_source_reset_settings(input, newSettings); // Tells any open source properties windows to perform a UI refresh obs_source_update_properties(input); return RequestResult::Success(); } /** * Gets the audio mute state of an input. * * @requestField inputName | String | Name of input to get the mute state of * * @responseField inputMuted | Boolean | Whether the input is muted * * @requestType GetInputMute * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputMute(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); json responseData; responseData["inputMuted"] = obs_source_muted(input); return RequestResult::Success(responseData); } /** * Sets the audio mute state of an input. * * @requestField inputName | String | Name of the input to set the mute state of * @requestField inputMuted | Boolean | Whether to mute the input or not * * @requestType SetInputMute * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputMute(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateBoolean("inputMuted", statusCode, comment))) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); obs_source_set_muted(input, request.RequestData["inputMuted"]); return RequestResult::Success(); } /** * Toggles the audio mute state of an input. * * @requestField inputName | String | Name of the input to toggle the mute state of * * @responseField inputMuted | Boolean | Whether the input has been muted or unmuted * * @requestType ToggleInputMute * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::ToggleInputMute(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); bool inputMuted = !obs_source_muted(input); obs_source_set_muted(input, inputMuted); json responseData; responseData["inputMuted"] = inputMuted; return RequestResult::Success(responseData); } /** * Gets the current volume setting of an input. * * @requestField inputName | String | Name of the input to get the volume of * * @responseField inputVolumeMul | Number | Volume setting in mul * @responseField inputVolumeDb | Number | Volume setting in dB * * @requestType GetInputVolume * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputVolume(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); float inputVolumeMul = obs_source_get_volume(input); float inputVolumeDb = obs_mul_to_db(inputVolumeMul); if (inputVolumeDb == -INFINITY) inputVolumeDb = -100.0; json responseData; responseData["inputVolumeMul"] = inputVolumeMul; responseData["inputVolumeDb"] = inputVolumeDb; return RequestResult::Success(responseData); } /** * Sets the volume setting of an input. * * @requestField inputName | String | Name of the input to set the volume of * @requestField ?inputVolumeMul | Number | Volume setting in mul | >= 0, <= 20 | `inputVolumeDb` should be specified * @requestField ?inputVolumeDb | Number | Volume setting in dB | >= -100, <= 26 | `inputVolumeMul` should be specified * * @requestType SetInputVolume * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputVolume(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); bool hasMul = request.Contains("inputVolumeMul"); if (hasMul && !request.ValidateOptionalNumber("inputVolumeMul", statusCode, comment, 0, 20)) return RequestResult::Error(statusCode, comment); bool hasDb = request.Contains("inputVolumeDb"); if (hasDb && !request.ValidateOptionalNumber("inputVolumeDb", statusCode, comment, -100, 26)) return RequestResult::Error(statusCode, comment); if (hasMul && hasDb) return RequestResult::Error(RequestStatus::TooManyRequestFields, "You may only specify one volume field."); if (!hasMul && !hasDb) return RequestResult::Error(RequestStatus::MissingRequestField, "You must specify one volume field."); float inputVolumeMul; if (hasMul) inputVolumeMul = request.RequestData["inputVolumeMul"]; else inputVolumeMul = obs_db_to_mul(request.RequestData["inputVolumeDb"]); obs_source_set_volume(input, inputVolumeMul); return RequestResult::Success(); } /** * Gets the audio balance of an input. * * @requestField inputName | String | Name of the input to get the audio balance of * * @responseField inputAudioBalance | Number | Audio balance value from 0.0-1.0 * * @requestType GetInputAudioBalance * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputAudioBalance(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); json responseData; responseData["inputAudioBalance"] = obs_source_get_balance_value(input); return RequestResult::Success(responseData); } /** * Sets the audio balance of an input. * * @requestField inputName | String | Name of the input to set the audio balance of * @requestField inputAudioBalance | Number | New audio balance value | >= 0.0, <= 1.0 * * @requestType SetInputAudioBalance * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputAudioBalance(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateNumber("inputAudioBalance", statusCode, comment, 0.0, 1.0))) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); float inputAudioBalance = request.RequestData["inputAudioBalance"]; obs_source_set_balance_value(input, inputAudioBalance); return RequestResult::Success(); } /** * Gets the audio sync offset of an input. * * Note: The audio sync offset can be negative too! * * @requestField inputName | String | Name of the input to get the audio sync offset of * * @responseField inputAudioSyncOffset | Number | Audio sync offset in milliseconds * * @requestType GetInputAudioSyncOffset * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputAudioSyncOffset(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); json responseData; // Offset is stored in nanoseconds in OBS. responseData["inputAudioSyncOffset"] = obs_source_get_sync_offset(input) / 1000000; return RequestResult::Success(responseData); } /** * Sets the audio sync offset of an input. * * @requestField inputName | String | Name of the input to set the audio sync offset of * @requestField inputAudioSyncOffset | Number | New audio sync offset in milliseconds | >= -950, <= 20000 * * @requestType SetInputAudioSyncOffset * @complexity 3 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputAudioSyncOffset(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateNumber("inputAudioSyncOffset", statusCode, comment, -950, 20000))) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); int64_t syncOffset = request.RequestData["inputAudioSyncOffset"]; obs_source_set_sync_offset(input, syncOffset * 1000000); return RequestResult::Success(); } /** * Gets the audio monitor type of an input. * * The available audio monitor types are: * * - `OBS_MONITORING_TYPE_NONE` * - `OBS_MONITORING_TYPE_MONITOR_ONLY` * - `OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT` * * @requestField inputName | String | Name of the input to get the audio monitor type of * * @responseField monitorType | String | Audio monitor type * * @requestType GetInputAudioMonitorType * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputAudioMonitorType(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); json responseData; responseData["monitorType"] = obs_source_get_monitoring_type(input); return RequestResult::Success(responseData); } /** * Sets the audio monitor type of an input. * * @requestField inputName | String | Name of the input to set the audio monitor type of * @requestField monitorType | String | Audio monitor type * * @requestType SetInputAudioMonitorType * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputAudioMonitorType(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("monitorType", statusCode, comment))) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); if (!obs_audio_monitoring_available()) return RequestResult::Error(RequestStatus::InvalidResourceState, "Audio monitoring is not available on this platform."); enum obs_monitoring_type monitorType; std::string monitorTypeString = request.RequestData["monitorType"]; if (monitorTypeString == "OBS_MONITORING_TYPE_NONE") monitorType = OBS_MONITORING_TYPE_NONE; else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_ONLY") monitorType = OBS_MONITORING_TYPE_MONITOR_ONLY; else if (monitorTypeString == "OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT") monitorType = OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT; else return RequestResult::Error(RequestStatus::InvalidRequestField, std::string("Unknown monitor type: ") + monitorTypeString); obs_source_set_monitoring_type(input, monitorType); return RequestResult::Success(); } /** * Gets the enable state of all audio tracks of an input. * * @requestField inputName | String | Name of the input * * @responseField inputAudioTracks | Object | Object of audio tracks and associated enable states * * @requestType GetInputAudioTracks * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputAudioTracks(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); long long tracks = obs_source_get_audio_mixers(input); json inputAudioTracks; for (long long i = 0; i < MAX_AUDIO_MIXES; i++) { inputAudioTracks[std::to_string(i + 1)] = (bool)((tracks >> i) & 1); } json responseData; responseData["inputAudioTracks"] = inputAudioTracks; return RequestResult::Success(responseData); } /** * Sets the enable state of audio tracks of an input. * * @requestField inputName | String | Name of the input * @requestField inputAudioTracks | Object | Track settings to apply * * @requestType SetInputAudioTracks * @complexity 2 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::SetInputAudioTracks(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!input || !request.ValidateObject("inputAudioTracks", statusCode, comment)) return RequestResult::Error(statusCode, comment); if (!(obs_source_get_output_flags(input) & OBS_SOURCE_AUDIO)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The specified input does not support audio."); json inputAudioTracks = request.RequestData["inputAudioTracks"]; uint32_t mixers = obs_source_get_audio_mixers(input); for (uint32_t i = 0; i < MAX_AUDIO_MIXES; i++) { std::string track = std::to_string(i + 1); if (!Utils::Json::Contains(inputAudioTracks, track)) continue; if (!inputAudioTracks[track].is_boolean()) return RequestResult::Error(RequestStatus::InvalidRequestFieldType, "The value of one of your tracks is not a boolean."); bool enabled = inputAudioTracks[track]; if (enabled) mixers |= (1 << i); else mixers &= ~(1 << i); } // Decided that checking if tracks have actually changed is unnecessary obs_source_set_audio_mixers(input, mixers); return RequestResult::Success(); } /** * Gets the items of a list property from an input's properties. * * Note: Use this in cases where an input provides a dynamic, selectable list of items. For example, display capture, where it provides a list of available displays. * * @requestField inputName | String | Name of the input * @requestField propertyName | String | Name of the list property to get the items of * * @responseField propertyItems | Array | Array of items in the list property * * @requestType GetInputPropertiesListPropertyItems * @complexity 4 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::GetInputPropertiesListPropertyItems(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("propertyName", statusCode, comment))) return RequestResult::Error(statusCode, comment); std::string propertyName = request.RequestData["propertyName"]; OBSPropertiesAutoDestroy inputProperties = obs_source_properties(input); obs_property_t *property = obs_properties_get(inputProperties, propertyName.c_str()); if (!property) return RequestResult::Error(RequestStatus::ResourceNotFound, "Unable to find a property by that name."); if (obs_property_get_type(property) != OBS_PROPERTY_LIST) return RequestResult::Error(RequestStatus::InvalidResourceType, "The property found is not a list."); json responseData; responseData["propertyItems"] = Utils::Obs::ArrayHelper::GetListPropertyItems(property); return RequestResult::Success(responseData); } /** * Presses a button in the properties of an input. * * Some known `propertyName` values are: * * - `refreshnocache` - Browser source reload button * * Note: Use this in cases where there is a button in the properties of an input that cannot be accessed in any other way. For example, browser sources, where there is a refresh button. * * @requestField inputName | String | Name of the input * @requestField propertyName | String | Name of the button property to press * * @requestType PressInputPropertiesButton * @complexity 4 * @rpcVersion -1 * @initialVersion 5.0.0 * @api requests * @category inputs */ RequestResult RequestHandler::PressInputPropertiesButton(const Request &request) { RequestStatus::RequestStatus statusCode; std::string comment; OBSSourceAutoRelease input = request.ValidateInput("inputName", statusCode, comment); if (!(input && request.ValidateString("propertyName", statusCode, comment))) return RequestResult::Error(statusCode, comment); std::string propertyName = request.RequestData["propertyName"]; OBSPropertiesAutoDestroy inputProperties = obs_source_properties(input); obs_property_t *property = obs_properties_get(inputProperties, propertyName.c_str()); if (!property) return RequestResult::Error(RequestStatus::ResourceNotFound, "Unable to find a property by that name."); if (obs_property_get_type(property) != OBS_PROPERTY_BUTTON) return RequestResult::Error(RequestStatus::InvalidResourceType, "The property found is not a button."); if (!obs_property_enabled(property)) return RequestResult::Error(RequestStatus::InvalidResourceState, "The property item found is not enabled."); obs_property_button_clicked(property, input); return RequestResult::Success(); }