#include "WSRequestHandler.h" /** * @typedef {Object} `Output` * @property {String} `name` Output name * @property {String} `type` Output type/kind * @property {int} `width` Video output width * @property {int} `height` Video output height * @property {Object} `flags` Output flags * @property {int} `flags.rawValue` Raw flags value * @property {boolean} `flags.audio` Output uses audio * @property {boolean} `flags.video` Output uses video * @property {boolean} `flags.encoded` Output is encoded * @property {boolean} `flags.multiTrack` Output uses several audio tracks * @property {boolean} `flags.service` Output uses a service * @property {Object} `settings` Output name * @property {boolean} `active` Output status (active or not) * @property {boolean} `reconnecting` Output reconnection status (reconnecting or not) * @property {double} `congestion` Output congestion * @property {int} `totalFrames` Number of frames sent * @property {int} `droppedFrames` Number of frames dropped * @property {int} `totalBytes` Total bytes sent */ obs_data_t* getOutputInfo(obs_output_t* output) { if (!output) { return nullptr; } OBSDataAutoRelease settings = obs_output_get_settings(output); uint32_t rawFlags = obs_output_get_flags(output); OBSDataAutoRelease flags = obs_data_create(); obs_data_set_int(flags, "rawValue", rawFlags); obs_data_set_bool(flags, "audio", rawFlags & OBS_OUTPUT_AUDIO); obs_data_set_bool(flags, "video", rawFlags & OBS_OUTPUT_VIDEO); obs_data_set_bool(flags, "encoded", rawFlags & OBS_OUTPUT_ENCODED); obs_data_set_bool(flags, "multiTrack", rawFlags & OBS_OUTPUT_MULTI_TRACK); obs_data_set_bool(flags, "service", rawFlags & OBS_OUTPUT_SERVICE); obs_data_t* data = obs_data_create(); obs_data_set_string(data, "name", obs_output_get_name(output)); obs_data_set_string(data, "type", obs_output_get_id(output)); obs_data_set_int(data, "width", obs_output_get_width(output)); obs_data_set_int(data, "height", obs_output_get_height(output)); obs_data_set_obj(data, "flags", flags); obs_data_set_obj(data, "settings", settings); obs_data_set_bool(data, "active", obs_output_active(output)); obs_data_set_bool(data, "reconnecting", obs_output_reconnecting(output)); obs_data_set_double(data, "congestion", obs_output_get_congestion(output)); obs_data_set_int(data, "totalFrames", obs_output_get_total_frames(output)); obs_data_set_int(data, "droppedFrames", obs_output_get_frames_dropped(output)); obs_data_set_int(data, "totalBytes", obs_output_get_total_bytes(output)); return data; } HandlerResponse findOutputOrFail(WSRequestHandler* req, std::function callback) { if (!req->hasField("outputName")) { return req->SendErrorResponse("missing request parameters"); } const char* outputName = obs_data_get_string(req->parameters(), "outputName"); OBSOutputAutoRelease output = obs_get_output_by_name(outputName); if (!output) { return req->SendErrorResponse("specified output doesn't exist"); } return callback(output); } /** * List existing outputs * * @return {Array} `outputs` Outputs list * * @api requests * @name ListOutputs * @category outputs * @since 4.7.0 */ HandlerResponse WSRequestHandler::HandleListOutputs(WSRequestHandler* req) { OBSDataArrayAutoRelease outputs = obs_data_array_create(); obs_enum_outputs([](void* param, obs_output_t* output) { obs_data_array_t* outputs = reinterpret_cast(param); OBSDataAutoRelease outputInfo = getOutputInfo(output); obs_data_array_push_back(outputs, outputInfo); return true; }, outputs); OBSDataAutoRelease fields = obs_data_create(); obs_data_set_array(fields, "outputs", outputs); return req->SendOKResponse(fields); } /** * Get information about a single output * * @param {String} `outputName` Output name * * @return {Output} `outputInfo` Output info * * @api requests * @name GetOutputInfo * @category outputs * @since 4.7.0 */ HandlerResponse WSRequestHandler::HandleGetOutputInfo(WSRequestHandler* req) { return findOutputOrFail(req, [req](obs_output_t* output) { OBSDataAutoRelease outputInfo = getOutputInfo(output); OBSDataAutoRelease fields = obs_data_create(); obs_data_set_obj(fields, "outputInfo", outputInfo); return req->SendOKResponse(fields); }); } /** * Start an output * * @param {String} `outputName` Output name * * @api requests * @name StartOutput * @category outputs * @since 4.7.0 */ HandlerResponse WSRequestHandler::HandleStartOutput(WSRequestHandler* req) { return findOutputOrFail(req, [req](obs_output_t* output) { if (obs_output_active(output)) { return req->SendErrorResponse("output already active"); } bool success = obs_output_start(output); if (!success) { QString lastError = obs_output_get_last_error(output); QString errorMessage = QString("output start failed: %1").arg(lastError); return req->SendErrorResponse(errorMessage); } return req->SendOKResponse(); }); } /** * Stop an output * * @param {String} `outputName` Output name * @param {boolean (optional)} `force` Force stop (default: false) * * @api requests * @name StopOutput * @category outputs * @since 4.7.0 */ HandlerResponse WSRequestHandler::HandleStopOutput(WSRequestHandler* req) { return findOutputOrFail(req, [req](obs_output_t* output) { if (!obs_output_active(output)) { return req->SendErrorResponse("output not active"); } bool forceStop = obs_data_get_bool(req->data, "force"); if (forceStop) { obs_output_force_stop(output); } else { obs_output_stop(output); } return req->SendOKResponse(); }); }