Skip to content

Commit

Permalink
requesthandler: Add Outputs requests
Browse files Browse the repository at this point in the history
Co-authored-by: Ruggero Tomaselli <ruggerotomaselli@gmail.com>
Co-authored-by: tt2468 <tt2468@irltoolkit.com>
  • Loading branch information
3 people committed Jul 2, 2022
1 parent 14a1547 commit 6af3af6
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 3 deletions.
7 changes: 7 additions & 0 deletions src/requesthandler/RequestHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ const std::unordered_map<std::string, RequestMethodHandler> RequestHandler::_han
{"StopReplayBuffer", &RequestHandler::StopReplayBuffer},
{"SaveReplayBuffer", &RequestHandler::SaveReplayBuffer},
{"GetLastReplayBufferReplay", &RequestHandler::GetLastReplayBufferReplay},
{"GetOutputList", &RequestHandler::GetOutputList},
{"GetOutputStatus", &RequestHandler::GetOutputStatus},
{"ToggleOutput", &RequestHandler::ToggleOutput},
{"StartOutput", &RequestHandler::StartOutput},
{"StopOutput", &RequestHandler::StopOutput},
{"GetOutputSettings", &RequestHandler::GetOutputSettings},
{"SetOutputSettings", &RequestHandler::SetOutputSettings},

// Stream
{"GetStreamStatus", &RequestHandler::GetStreamStatus},
Expand Down
7 changes: 7 additions & 0 deletions src/requesthandler/RequestHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ class RequestHandler {
RequestResult StopReplayBuffer(const Request &);
RequestResult SaveReplayBuffer(const Request &);
RequestResult GetLastReplayBufferReplay(const Request &);
RequestResult GetOutputList(const Request &);
RequestResult GetOutputStatus(const Request &);
RequestResult ToggleOutput(const Request &);
RequestResult StartOutput(const Request &);
RequestResult StopOutput(const Request &);
RequestResult GetOutputSettings(const Request &);
RequestResult SetOutputSettings(const Request &);

// Stream
RequestResult GetStreamStatus(const Request &);
Expand Down
211 changes: 211 additions & 0 deletions src/requesthandler/RequestHandler_Outputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,214 @@ RequestResult RequestHandler::GetLastReplayBufferReplay(const Request &)
responseData["savedReplayPath"] = Utils::Obs::StringHelper::GetLastReplayBufferFileName();
return RequestResult::Success(responseData);
}

/**
* Gets the list of available outputs.
*
* @requestType GetOutputList
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::GetOutputList(const Request &)
{
json responseData;
responseData["outputs"] = Utils::Obs::ArrayHelper::GetOutputList();
return RequestResult::Success(responseData);
}

/**
* Gets the status of an output.
*
* @requestField outputName | String | Output name
*
* @responseField outputActive | Boolean | Whether the output is active
* @responseField outputReconnecting | Boolean | Whether the output is reconnecting
* @responseField outputTimecode | String | Current formatted timecode string for the output
* @responseField outputDuration | Number | Current duration in milliseconds for the output
* @responseField outputCongestion | Number | Congestion of the output
* @responseField outputBytes | Number | Number of bytes sent by the output
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
*
* @requestType GetOutputStatus
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::GetOutputStatus(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);

uint64_t outputDuration = Utils::Obs::NumberHelper::GetOutputDuration(output);

json responseData;
responseData["outputActive"] = obs_output_active(output);
responseData["outputReconnecting"] = obs_output_reconnecting(output);
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
responseData["outputDuration"] = outputDuration;
responseData["outputCongestion"] = obs_output_get_congestion(output);
responseData["outputBytes"] = obs_output_get_total_bytes(output);
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(output);
responseData["outputTotalFrames"] = obs_output_get_total_frames(output);

return RequestResult::Success(responseData);
}

/**
* Toggles the status of an output.
*
* @requestField outputName | String | Output name
*
* @responseField outputActive | Boolean | Whether the output is active
*
* @requestType ToggleOutput
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::ToggleOutput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);

bool outputActive = obs_output_active(output);
if (outputActive)
obs_output_stop(output);
else
obs_output_start(output);

json responseData;
responseData["outputActive"] = !outputActive;
return RequestResult::Success(responseData);
}

/**
* Starts an output.
*
* @requestField outputName | String | Output name
*
* @requestType StartOutput
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StartOutput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);

if (obs_output_active(output))
return RequestResult::Error(RequestStatus::OutputRunning);

obs_output_start(output);

return RequestResult::Success();
}

/**
* Stops an output.
*
* @requestField outputName | String | Output name
*
* @requestType StopOutput
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::StopOutput(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);

if (!obs_output_active(output))
return RequestResult::Error(RequestStatus::OutputNotRunning);

obs_output_stop(output);

return RequestResult::Success();
}

/**
* Gets the settings of an output.
*
* @requestField outputName | String | Output name
*
* @responseField outputSettings | Object | Output settings
*
* @requestType GetOutputSettings
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::GetOutputSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!output)
return RequestResult::Error(statusCode, comment);

OBSDataAutoRelease outputSettings = obs_output_get_settings(output);

json responseData;
responseData["outputSettings"] = Utils::Json::ObsDataToJson(outputSettings);
return RequestResult::Success(responseData);
}

/**
* Sets the settings of an output.
*
* @requestField outputName | String | Output name
* @requestField outputSettings | Object | Output settings
*
* @requestType SetOutputSettings
* @complexity 4
* @rpcVersion -1
* @initialVersion 5.0.0
* @api requests
* @category outputs
*/
RequestResult RequestHandler::SetOutputSettings(const Request &request)
{
RequestStatus::RequestStatus statusCode;
std::string comment;
OBSOutputAutoRelease output = request.ValidateOutput("outputName", statusCode, comment);
if (!(output && request.ValidateObject("outputSettings", statusCode, comment, true)))
return RequestResult::Error(statusCode, comment);

OBSDataAutoRelease newSettings = Utils::Json::JsonToObsData(request.RequestData["outputSettings"]);
if (!newSettings)
// This should never happen
return RequestResult::Error(RequestStatus::RequestProcessingFailed,
"An internal data conversion operation failed. Please report this!");

obs_output_update(output, newSettings);

return RequestResult::Success();
}
2 changes: 2 additions & 0 deletions src/requesthandler/RequestHandler_Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ with this program. If not, see <https://www.gnu.org/licenses/>
* @responseField outputReconnecting | Boolean | Whether the output is currently reconnecting
* @responseField outputTimecode | String | Current formatted timecode string for the output
* @responseField outputDuration | Number | Current duration in milliseconds for the output
* @responseField outputCongestion | Number | Congestion of the output
* @responseField outputBytes | Number | Number of bytes sent by the output
* @responseField outputSkippedFrames | Number | Number of frames skipped by the output's process
* @responseField outputTotalFrames | Number | Total number of frames delivered by the output's process
Expand All @@ -48,6 +49,7 @@ RequestResult RequestHandler::GetStreamStatus(const Request &)
responseData["outputReconnecting"] = obs_output_reconnecting(streamOutput);
responseData["outputTimecode"] = Utils::Obs::StringHelper::DurationToTimecode(outputDuration);
responseData["outputDuration"] = outputDuration;
responseData["outputCongestion"] = obs_output_get_congestion(streamOutput);
responseData["outputBytes"] = (uint64_t)obs_output_get_total_bytes(streamOutput);
responseData["outputSkippedFrames"] = obs_output_get_frames_dropped(streamOutput);
responseData["outputTotalFrames"] = obs_output_get_total_frames(streamOutput);
Expand Down
9 changes: 6 additions & 3 deletions src/requesthandler/RequestHandler_Ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ RequestResult RequestHandler::OpenVideoMixProjector(const Request &request)
else if (videoMixType == "OBS_WEBSOCKET_VIDEO_MIX_TYPE_MULTIVIEW")
projectorType = "Multiview";
else
return RequestResult::Error(RequestStatus::InvalidRequestField, "The field `videoMixType` has an invalid enum value.");
return RequestResult::Error(RequestStatus::InvalidRequestField,
"The field `videoMixType` has an invalid enum value.");

int monitorIndex = -1;
if (request.Contains("monitorIndex")) {
Expand All @@ -246,7 +247,8 @@ RequestResult RequestHandler::OpenVideoMixProjector(const Request &request)
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
return RequestResult::Error(statusCode, comment);
if (monitorIndex != -1)
return RequestResult::Error(RequestStatus::TooManyRequestFields, "`monitorIndex` and `projectorGeometry` are mutually exclusive.");
return RequestResult::Error(RequestStatus::TooManyRequestFields,
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
projectorGeometry = request.RequestData["projectorGeometry"];
}

Expand Down Expand Up @@ -291,7 +293,8 @@ RequestResult RequestHandler::OpenSourceProjector(const Request &request)
if (!request.ValidateOptionalString("projectorGeometry", statusCode, comment))
return RequestResult::Error(statusCode, comment);
if (monitorIndex != -1)
return RequestResult::Error(RequestStatus::TooManyRequestFields, "`monitorIndex` and `projectorGeometry` are mutually exclusive.");
return RequestResult::Error(RequestStatus::TooManyRequestFields,
"`monitorIndex` and `projectorGeometry` are mutually exclusive.");
projectorGeometry = request.RequestData["projectorGeometry"];
}

Expand Down
18 changes: 18 additions & 0 deletions src/requesthandler/rpc/Request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,21 @@ obs_sceneitem_t *Request::ValidateSceneItem(const std::string &sceneKeyName, con
obs_sceneitem_addref(sceneItem);
return sceneItem;
}

obs_output_t *Request::ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const
{
if (!ValidateString(keyName, statusCode, comment))
return nullptr;

std::string outputName = RequestData[keyName];

obs_output_t *ret = obs_get_output_by_name(outputName.c_str());
if (!ret) {
statusCode = RequestStatus::ResourceNotFound;
comment = std::string("No output was found with the name `") + outputName + "`.";
return nullptr;
}

return ret;
}
2 changes: 2 additions & 0 deletions src/requesthandler/rpc/Request.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ struct Request {
obs_sceneitem_t *ValidateSceneItem(const std::string &sceneKeyName, const std::string &sceneItemIdKeyName,
RequestStatus::RequestStatus &statusCode, std::string &comment,
const ObsWebSocketSceneFilter filter = OBS_WEBSOCKET_SCENE_FILTER_SCENE_ONLY) const;
obs_output_t *ValidateOutput(const std::string &keyName, RequestStatus::RequestStatus &statusCode,
std::string &comment) const;

std::string RequestType;
bool HasRequestData;
Expand Down
1 change: 1 addition & 0 deletions src/utils/Obs.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ namespace Utils {
std::vector<json> GetSceneTransitionList();
std::vector<json> GetSourceFilterList(obs_source_t *source);
std::vector<std::string> GetFilterKindList();
std::vector<json> GetOutputList();
}

namespace ObjectHelper {
Expand Down
32 changes: 32 additions & 0 deletions src/utils/Obs_ArrayHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,35 @@ std::vector<json> Utils::Obs::ArrayHelper::GetSourceFilterList(obs_source_t *sou

return filters;
}

std::vector<json> Utils::Obs::ArrayHelper::GetOutputList()
{
std::vector<json> outputs;

auto cb = [](void *param, obs_output_t *output) {
auto outputs = reinterpret_cast<std::vector<json> *>(param);

auto rawFlags = obs_output_get_flags(output);
json flags;
flags["OBS_OUTPUT_AUDIO"] = !!(rawFlags & OBS_OUTPUT_AUDIO);
flags["OBS_OUTPUT_VIDEO"] = !!(rawFlags & OBS_OUTPUT_VIDEO);
flags["OBS_OUTPUT_ENCODED"] = !!(rawFlags & OBS_OUTPUT_ENCODED);
flags["OBS_OUTPUT_MULTI_TRACK"] = !!(rawFlags & OBS_OUTPUT_MULTI_TRACK);
flags["OBS_OUTPUT_SERVICE"] = !!(rawFlags & OBS_OUTPUT_SERVICE);

json outputJson;
outputJson["outputName"] = obs_output_get_name(output);
outputJson["outputKind"] = obs_output_get_id(output);
outputJson["outputWidth"] = obs_output_get_width(output);
outputJson["outputHeight"] = obs_output_get_height(output);
outputJson["outputActive"] = obs_output_active(output);
outputJson["outputFlags"] = flags;

outputs->push_back(outputJson);
return true;
};

obs_enum_outputs(cb, &outputs);

return outputs;
}

0 comments on commit 6af3af6

Please sign in to comment.