diff --git a/git-teaspeak b/git-teaspeak index b07d170..366ba5b 160000 --- a/git-teaspeak +++ b/git-teaspeak @@ -1 +1 @@ -Subproject commit b07d170e7b5260d341db2617187e0df7edc71b3c +Subproject commit 366ba5ba42e7503afcb2e81b367209c9e4f5c5d7 diff --git a/rtclib b/rtclib index ea13ab4..2c08b87 160000 --- a/rtclib +++ b/rtclib @@ -1 +1 @@ -Subproject commit ea13ab489529ef0ebc72c15505c6be52ed484269 +Subproject commit 2c08b8759268095b96f3904757300905db1ea61d diff --git a/server/src/TS3ServerClientManager.cpp b/server/src/TS3ServerClientManager.cpp index 4e00e6c..54ffb11 100644 --- a/server/src/TS3ServerClientManager.cpp +++ b/server/src/TS3ServerClientManager.cpp @@ -478,7 +478,7 @@ void VirtualServer::client_move( } if(auto client{dynamic_pointer_cast(target)}; client) { /* Start normal broadcasting, what the client expects */ - this->rtc_server().start_broadcast(client->rtc_client_id, 1, 1); + this->rtc_server().start_broadcast_audio(client->rtc_client_id, 1); } } else { /* client left the server */ diff --git a/server/src/client/ConnectedClient.h b/server/src/client/ConnectedClient.h index 4c3eb5a..b3dd3cc 100644 --- a/server/src/client/ConnectedClient.h +++ b/server/src/client/ConnectedClient.h @@ -619,7 +619,7 @@ namespace ts { //handleCommandDummy_ConnectFailed //handleCommandDummy_ConnectionLost - //Not needed - completly useless + //Not needed - completely useless //CMD_TODO handleCommandCustomInfo //CMD_TODO handleCommandCustomSearch //CMD_TODO serverquerycmd diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index 35720d1..abdae00 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -15,6 +15,7 @@ #include "misc/timer.h" #include "../manager/ActionLogger.h" #include "./voice/VoiceClient.h" +#include "../rtc/imports.h" using namespace std::chrono; using namespace ts; @@ -620,13 +621,22 @@ command_result SpeakingClient::handleCommand(Command &command) { } else if(command.command() == "rtcicecandidate") { return this->handleCommandRtcIceCandidate(command); } else if(command.command() == "rtcbroadcast") { + /* TODO: Remove this command once the first 1.5.0 stable is out */ return this->handleCommandRtcBroadcast(command); } else if(command.command() == "rtcsessionreset") { return this->handleCommandRtcSessionReset(command); + } else if(command.command() == "broadcastaudio") { + return this->handleCommandBroadcastAudio(command); + } else if(command.command() == "broadcastvideo") { + return this->handleCommandBroadcastVideo(command); } else if(command.command() == "broadcastvideojoin") { return this->handleCommandBroadcastVideoJoin(command); } else if(command.command() == "broadcastvideoleave") { return this->handleCommandBroadcastVideoLeave(command); + } else if(command.command() == "broadcastvideoconfig") { + return this->handleCommandBroadcastVideoConfig(command); + } else if(command.command() == "broadcastvideoconfigure") { + return this->handleCommandBroadcastVideoConfigure(command); } } return ConnectedClient::handleCommand(command); @@ -676,7 +686,7 @@ command_result SpeakingClient::handleCommandRtcSessionReset(Command &command) { this->server->rtc_server().reset_rtp_session(this->rtc_client_id); if(this->getType() == ClientType::CLIENT_TEASPEAK) { /* registering the broadcast again since rtp session reset resets the broadcasts as well */ - this->server->rtc_server().start_broadcast(this->rtc_client_id, 1, 1); + this->server->rtc_server().start_broadcast_audio(this->rtc_client_id, 1); } return command_result{error::ok}; } @@ -696,6 +706,128 @@ command_result SpeakingClient::handleCommandRtcIceCandidate(Command &command) { return command_result{error::ok}; } +ts::command_result parse_broadcast_options(ParameterBulk &cmd, VideoBroadcastOptions& options, bool requires_all) { + if(cmd.has("broadcast_bitrate_max")) { + options.update_mask |= VideoBroadcastOptions::kOptionBitrate; + options.bitrate = cmd["broadcast_bitrate_max"]; + } else if(requires_all) { + return ts::command_result{error::parameter_missing, "broadcast_bitrate_max"}; + } + + if(cmd.has("broadcast_keyframe_interval")) { + options.update_mask |= VideoBroadcastOptions::kOptionKeyframeInterval; + options.keyframe_interval = cmd["broadcast_keyframe_interval"]; + } else if(requires_all) { + return ts::command_result{error::parameter_missing, "broadcast_keyframe_interval"}; + } + + return ts::command_result{error::ok}; +} + +void simplify_broadcast_options(const VideoBroadcastOptions& current_options, VideoBroadcastOptions& target_options) { + if(target_options.bitrate == current_options.bitrate) { + target_options.update_mask &= ~VideoBroadcastOptions::kOptionBitrate; + } + + if(target_options.keyframe_interval == current_options.keyframe_interval) { + target_options.update_mask &= ~VideoBroadcastOptions::kOptionKeyframeInterval; + } +} + +/** + * Test if the client has permissions to use the target broadcast options + * @param client + * @param channel_id + * @param options + * @return + */ +ts::command_result test_broadcast_options(SpeakingClient& client, ChannelId channel_id, const VideoBroadcastOptions& options) { + if(options.update_mask & VideoBroadcastOptions::kOptionBitrate) { + auto required_value = options.bitrate == 0 ? -1 : (permission::PermissionValue) (options.bitrate / 1000); + if(!permission::v2::permission_granted(required_value, client.calculate_permission(permission::i_video_max_kbps, channel_id))) { + return ts::command_result{permission::i_video_max_kbps}; + } + } + + return ts::command_result{error::ok}; +} + +inline command_result broadcast_start_result_to_command_result(rtc::BroadcastStartResult broadcast_result) { + switch(broadcast_result) { + case rtc::BroadcastStartResult::Success: + return ts::command_result{error::ok}; + case rtc::BroadcastStartResult::InvalidBroadcastType: + return ts::command_result{error::parameter_invalid, "type"}; + case rtc::BroadcastStartResult::InvalidStreamId: + return ts::command_result{error::rtc_missing_target_channel}; + case rtc::BroadcastStartResult::ClientHasNoChannel: + return ts::command_result{error::vs_critical, "no channel"}; + case rtc::BroadcastStartResult::InvalidClient: + return ts::command_result{error::vs_critical, "invalid client"}; + case rtc::BroadcastStartResult::UnknownError: + default: + return ts::command_result{error::vs_critical, "unknown error"}; + } +} + +command_result SpeakingClient::handleCommandBroadcastAudio(Command &command) { + CMD_REQ_SERVER; + CMD_CHK_AND_INC_FLOOD_POINTS(5); + + auto ssrc = command[0].has("ssrc") ? command["ssrc"].as() : (uint32_t) 0; + auto broadcast_result = this->server->rtc_server().start_broadcast_audio(this->rtc_client_id, ssrc); + return broadcast_start_result_to_command_result(broadcast_result); +} + +command_result SpeakingClient::handleCommandBroadcastVideo(Command &command) { + CMD_REQ_SERVER; + CMD_CHK_AND_INC_FLOOD_POINTS(15); + + auto ssrc = command[0].has("ssrc") ? command["ssrc"].as() : (uint32_t) 0; + auto type = (rtc::VideoBroadcastType) command["type"].as(); + + switch(type) { + case rtc::VideoBroadcastType::Screen: + if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_screen, this->getChannelId()), false)) { + return ts::command_result{permission::b_video_screen}; + } + + break; + + + case rtc::VideoBroadcastType::Camera: + if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_camera, this->getChannelId()), false)) { + return ts::command_result{permission::b_video_camera}; + } + + break; + + default: + return ts::command_result{error::parameter_invalid, "type"}; + } + + VideoBroadcastOptions options; + memset(&options, 0, sizeof(options)); + if(ssrc != 0) { + ts::command_result result; + + result.reset(parse_broadcast_options(command[0], options, true)); + if(result.has_error()) { + return result; + } + + result.reset(test_broadcast_options(*this, this->getChannelId(), options)); + if(result.has_error()) { + return result; + } + + result.release_data(); + } + + auto broadcast_result = this->server->rtc_server().start_broadcast_video(this->rtc_client_id, type, ssrc, &options); + return broadcast_start_result_to_command_result(broadcast_result); +} + command_result SpeakingClient::handleCommandRtcBroadcast(Command &command) { CMD_REQ_SERVER; CMD_CHK_AND_INC_FLOOD_POINTS(15); @@ -703,6 +835,7 @@ command_result SpeakingClient::handleCommandRtcBroadcast(Command &command) { std::vector> broadcasts{}; broadcasts.reserve(command.bulkCount()); + ts::command_result_bulk result{}; for(size_t index{0}; index < command.bulkCount(); index++) { auto& bulk = command[index]; @@ -716,54 +849,37 @@ command_result SpeakingClient::handleCommandRtcBroadcast(Command &command) { } broadcasts.push_back(std::make_tuple(type, ssrc)); - } - - /* TODO: Apply constraints like bandwidth? */ - - ts::command_result_bulk result{}; - for(size_t index{0}; index < command.bulkCount(); index++) { - auto broadcast_type = std::get<0>(broadcasts[index]); - switch(broadcast_type) { - case 2: - if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_camera, this->getChannelId()), false)) { - result.emplace_result(permission::b_video_camera); - continue; - } + switch(type) { + case 1: { + ts::Command cmd{""}; + cmd["ssrc"] = ssrc; + result.insert_result(this->handleCommandBroadcastAudio(cmd)); break; - - case 3: - if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_video_screen, this->getChannelId()), false)) { - result.emplace_result(permission::b_video_screen); - continue; - } + } + case 2: { + ts::Command cmd{""}; + cmd["broadcast_bitrate_max"] = 1500000; + cmd["broadcast_keyframe_interval"] = 7; + cmd["ssrc"] = ssrc; + cmd["type"] = (uint8_t) rtc::VideoBroadcastType::Camera; + result.insert_result(this->handleCommandBroadcastVideo(cmd)); break; - + } + case 3: { + ts::Command cmd{""}; + cmd["broadcast_bitrate_max"] = 1500000; + cmd["broadcast_keyframe_interval"] = 7; + cmd["ssrc"] = ssrc; + cmd["type"] = (uint8_t) rtc::VideoBroadcastType::Screen; + result.insert_result(this->handleCommandBroadcastVideo(cmd)); + break; + } default: - break; - } - - auto broadcast_result = this->server->rtc_server().start_broadcast(this->rtc_client_id, std::get<0>(broadcasts[index]), std::get<1>(broadcasts[index])); - switch(broadcast_result) { - case rtc::BroadcastStartResult::Success: - result.emplace_result(error::ok); - break; - case rtc::BroadcastStartResult::InvalidBroadcastType: result.emplace_result(error::parameter_invalid, "type"); break; - case rtc::BroadcastStartResult::InvalidStreamId: - result.emplace_result(error::rtc_missing_target_channel); - break; - case rtc::BroadcastStartResult::ClientHasNoChannel: - result.emplace_result(error::vs_critical, "no channel"); - break; - case rtc::BroadcastStartResult::InvalidClient: - result.emplace_result(error::vs_critical, "invalid client"); - break; - case rtc::BroadcastStartResult::UnknownError: - default: - result.emplace_result(error::vs_critical, "unknown error"); } } + return ts::command_result{std::move(result)}; } @@ -818,8 +934,6 @@ command_result SpeakingClient::handleCommandBroadcastVideoJoin(Command &cmd) { /* The client is free to join his own broadcast */ } - /* TODO: Configure the broadcast? */ - using VideoBroadcastJoinResult = rtc::VideoBroadcastJoinResult; switch(this->server->rtc_server().join_video_broadcast(this->rtc_client_id, broadcast_id, broadcast_type)) { case VideoBroadcastJoinResult::Success: @@ -848,4 +962,101 @@ command_result SpeakingClient::handleCommandBroadcastVideoLeave(Command &cmd) { this->server->rtc_server().leave_video_broadcast(this->rtc_client_id, broadcast_id, broadcast_type); return ts::command_result{error::ok}; +} + +command_result SpeakingClient::handleCommandBroadcastVideoConfig(Command &cmd) { + CMD_REQ_SERVER; + + auto broadcast_type = (rtc::VideoBroadcastType) cmd["bt"].as(); + + VideoBroadcastOptions options; + auto result = this->server->rtc_server().client_broadcast_video_config(this->rtc_client_id, broadcast_type, &options); + switch(result) { + case rtc::VideoBroadcastConfigureResult::Success: + break; + + case rtc::VideoBroadcastConfigureResult::InvalidBroadcast: + return ts::command_result{error::broadcast_invalid_id}; + + case rtc::VideoBroadcastConfigureResult::InvalidBroadcastType: + return ts::command_result{error::broadcast_invalid_type}; + + case rtc::VideoBroadcastConfigureResult::InvalidClient: + return ts::command_result{error::client_invalid_id}; + + case rtc::VideoBroadcastConfigureResult::UnknownError: + default: + return ts::command_result{error::vs_critical}; + } + + ts::command_builder notify{this->notify_response_command("notifybroadcastvideoconfig")}; + notify.put_unchecked(0, "bt", (uint8_t) broadcast_type); + notify.put_unchecked(0, "broadcast_keyframe_interval", options.keyframe_interval); + notify.put_unchecked(0, "broadcast_bitrate_max", options.bitrate); + this->sendCommand(notify); + + return ts::command_result{error::ok}; +} + +inline command_result broadcast_config_result_to_command_result(rtc::VideoBroadcastConfigureResult result) { + switch(result) { + case rtc::VideoBroadcastConfigureResult::Success: + return ts::command_result{error::ok}; + + case rtc::VideoBroadcastConfigureResult::InvalidBroadcast: + return ts::command_result{error::broadcast_invalid_id}; + + case rtc::VideoBroadcastConfigureResult::InvalidBroadcastType: + return ts::command_result{error::broadcast_invalid_type}; + + case rtc::VideoBroadcastConfigureResult::InvalidClient: + return ts::command_result{error::client_invalid_id}; + + case rtc::VideoBroadcastConfigureResult::UnknownError: + default: + return ts::command_result{error::vs_critical}; + } +} + +command_result SpeakingClient::handleCommandBroadcastVideoConfigure(Command &cmd) { + CMD_REQ_SERVER; + + auto broadcast_type = (rtc::VideoBroadcastType) cmd["bt"].as(); + + VideoBroadcastOptions current_options; + auto query_result = this->server->rtc_server().client_broadcast_video_config(this->rtc_client_id, broadcast_type, ¤t_options); + if(query_result != rtc::VideoBroadcastConfigureResult::Success) { + return broadcast_config_result_to_command_result(query_result); + } + + VideoBroadcastOptions options; + memset(&options, 0, sizeof(options)); + { + ts::command_result result; + + result.reset(parse_broadcast_options(cmd[0], options, false)); + if(result.has_error()) { + return result; + } + + simplify_broadcast_options(current_options, options); + result.reset(test_broadcast_options(*this, this->getChannelId(), options)); + if(result.has_error()) { + return result; + } + + result.release_data(); + } + + { + auto result = test_broadcast_options(*this, this->getChannelId(), options); + if(result.has_error()) { + return result; + } + + result.release_data(); + } + + auto result = this->server->rtc_server().client_broadcast_video_configure(this->rtc_client_id, broadcast_type, &options); + return broadcast_config_result_to_command_result(result); } \ No newline at end of file diff --git a/server/src/client/SpeakingClient.h b/server/src/client/SpeakingClient.h index 7f43529..7867d21 100644 --- a/server/src/client/SpeakingClient.h +++ b/server/src/client/SpeakingClient.h @@ -70,9 +70,13 @@ namespace ts::server { virtual command_result handleCommandRtcSessionDescribe(Command &command); virtual command_result handleCommandRtcSessionReset(Command &command); virtual command_result handleCommandRtcIceCandidate(Command &); - virtual command_result handleCommandRtcBroadcast(Command &); + virtual command_result handleCommandRtcBroadcast(Command &); /* Old method used 'till 1.5.0b4 */ + virtual command_result handleCommandBroadcastAudio(Command &); + virtual command_result handleCommandBroadcastVideo(Command &); virtual command_result handleCommandBroadcastVideoJoin(Command &); virtual command_result handleCommandBroadcastVideoLeave(Command &); + virtual command_result handleCommandBroadcastVideoConfig(Command &); + virtual command_result handleCommandBroadcastVideoConfigure(Command &); void triggerVoiceEnd(); inline void updateSpeak(bool onlyUpdate, const std::chrono::system_clock::time_point &time); diff --git a/server/src/client/command_handler/helpers.h b/server/src/client/command_handler/helpers.h index f5e9326..d4f4d00 100644 --- a/server/src/client/command_handler/helpers.h +++ b/server/src/client/command_handler/helpers.h @@ -156,6 +156,8 @@ inline bool permission_require_granted_value(ts::permission::PermissionType type case permission::i_client_ban_max_bantime: case permission::i_client_max_idletime: case permission::i_group_sort_id: + + case permission::i_video_max_kbps: return false; default: return true; diff --git a/server/src/manager/SqlDataManager.cpp b/server/src/manager/SqlDataManager.cpp index e1a6e93..bc8b9b2 100644 --- a/server/src/manager/SqlDataManager.cpp +++ b/server/src/manager/SqlDataManager.cpp @@ -45,7 +45,7 @@ if(!result && result.msg().find(ignore) == string::npos){ #define RESIZE_COLUMN(tblName, rowName, size) up vote EXECUTE("Could not change column size", "ALTER TABLE " tblName " ALTER COLUMN " rowName " varchar(" size ")"); #define CURRENT_DATABASE_VERSION 17 -#define CURRENT_PERMISSION_VERSION 7 +#define CURRENT_PERMISSION_VERSION 8 #define CLIENT_UID_LENGTH "64" #define CLIENT_NAME_LENGTH "128" @@ -841,6 +841,7 @@ bool SqlDataManager::update_permissions(std::string &error) { if(!auto_update(permission::update::type, name, {value, value != permNotGranted}, false, false, {granted, granted != permNotGranted})) \ return false; + /* Attention: Due to a mistake the "i_video_max_kbps" permission has bit and not kbit values. In version 7 we divide by 1000. */ do_auto_update(QUERY_ADMIN, "b_video_screen", 1, 100); do_auto_update(QUERY_ADMIN, "b_video_camera", 1, 100); do_auto_update(QUERY_ADMIN, "i_video_max_kbps", 20 * 1000 * 1000, 100); @@ -871,6 +872,14 @@ if(!auto_update(permission::update::type, name, {value, value != permNotGranted} perm_version(7); + case 7: + result = sql::command(this->sql(), "UPDATE `permissions` SET `value` = `value` / 1000 WHERE permId = 'i_video_max_kbps'").execute(); + if(!result) { + LOG_SQL_CMD(result); + return false; + } + + perm_version(8); default: break; } diff --git a/server/src/rtc/imports.h b/server/src/rtc/imports.h index b1c92d2..231c89d 100644 --- a/server/src/rtc/imports.h +++ b/server/src/rtc/imports.h @@ -45,6 +45,15 @@ struct RtpClientConfigureOptions { uint16_t stun_port; }; +struct VideoBroadcastOptions { + constexpr static auto kOptionBitrate{0x01}; + constexpr static auto kOptionKeyframeInterval{0x02}; + + uint32_t update_mask; + uint32_t bitrate; + uint32_t keyframe_interval; +}; + extern const char* librtc_version(); extern void librtc_free_str(const char* /* ptr */); @@ -79,14 +88,21 @@ extern void librtc_destroy_audio_source_supplier(void* /* sender */); extern uint32_t librtc_create_channel(void* /* server */); extern uint32_t librtc_assign_channel(void* /* server */, uint32_t /* client id */, uint32_t /* channel id */); -extern uint32_t librtc_client_broadcast(void* /* server */, uint32_t /* client id */, uint8_t /* broadcast type */, uint32_t /* stream id */); -extern uint32_t librtc_video_broadcast_join(void* /* server */, uint32_t /* client id */, uint32_t /* target client id */, uint8_t /* broadcast type */); -extern void librtc_video_broadcast_leave(void* /* server */, uint32_t /* client id */, uint32_t /* target client id */, uint8_t /* broadcast type */); extern void librtc_destroy_channel(void* /* server */, uint32_t /* channel */); +/* Audio functions */ +extern uint32_t librtc_client_broadcast_audio(void* /* server */, uint32_t /* client id */, uint32_t /* stream id */); extern const char* librtc_whisper_configure(void* /* server */, uint32_t /* client id */, uint32_t /* source stream id */, uint32_t* /* client ids */, uint32_t /* client id count */); extern void librtc_whisper_reset(void* /* server */, uint32_t /* client id */); +/* Video functions */ +extern uint32_t librtc_client_broadcast_video(void* /* server */, uint32_t /* client id */, uint8_t /* broadcast type */, uint32_t /* stream id */, const VideoBroadcastOptions* /* options */); +extern uint32_t librtc_client_broadcast_video_configure(void* /* callback data */, uint32_t /* client id */, uint8_t /* broadcast type */, const VideoBroadcastOptions* /* options */); +extern uint32_t librtc_client_broadcast_video_config(void* /* callback data */, uint32_t /* client id */, uint8_t /* broadcast type */, VideoBroadcastOptions* /* options */); + +extern uint32_t librtc_video_broadcast_join(void* /* server */, uint32_t /* client id */, uint32_t /* target client id */, uint8_t /* broadcast type */); +extern void librtc_video_broadcast_leave(void* /* server */, uint32_t /* client id */, uint32_t /* target client id */, uint8_t /* broadcast type */); + #ifdef __cplusplus }; #endif \ No newline at end of file diff --git a/server/src/rtc/lib.cpp b/server/src/rtc/lib.cpp index fb35bfc..a907b25 100644 --- a/server/src/rtc/lib.cpp +++ b/server/src/rtc/lib.cpp @@ -324,6 +324,30 @@ bool Server::client_video_stream_count(uint32_t client_id, uint32_t *camera, uin return librtc_client_video_stream_count(this->server_ptr, client_id, camera, desktop) == 0; } +VideoBroadcastConfigureResult Server::client_broadcast_video_configure(RTCClientId client_id, VideoBroadcastType broadcast_type, + const VideoBroadcastOptions *options) { + auto result = librtc_client_broadcast_video_configure(this->server_ptr, client_id, (uint8_t) broadcast_type, options); + switch(result) { + case 0x00: return VideoBroadcastConfigureResult::Success; + case 0x01: return VideoBroadcastConfigureResult::InvalidBroadcastType; + case 0x02: return VideoBroadcastConfigureResult::InvalidClient; + case 0x03: return VideoBroadcastConfigureResult::InvalidBroadcast; + default: return VideoBroadcastConfigureResult::UnknownError; + } +} + +VideoBroadcastConfigureResult Server::client_broadcast_video_config(RTCClientId client_id, VideoBroadcastType broadcast_type, + VideoBroadcastOptions *options) { + auto result = librtc_client_broadcast_video_config(this->server_ptr, client_id, (uint8_t) broadcast_type, options); + switch(result) { + case 0x00: return VideoBroadcastConfigureResult::Success; + case 0x01: return VideoBroadcastConfigureResult::InvalidBroadcastType; + case 0x02: return VideoBroadcastConfigureResult::InvalidClient; + case 0x03: return VideoBroadcastConfigureResult::InvalidBroadcast; + default: return VideoBroadcastConfigureResult::UnknownError; + } +} + void Server::reset_rtp_session(RTCClientId client_id) { librtc_reset_rtp_session(this->server_ptr, client_id); } @@ -368,7 +392,7 @@ uint32_t Server::create_channel() { return librtc_create_channel(this->server_ptr); } -ChannelAssignResult Server::assign_channel(uint32_t client_id, uint32_t channel_id) { +ChannelAssignResult Server::assign_channel(RTCClientId client_id, RTCChannelId channel_id) { auto result = librtc_assign_channel(this->server_ptr, client_id, channel_id); switch(result) { case 0x00: return ChannelAssignResult::Success; @@ -378,8 +402,22 @@ ChannelAssignResult Server::assign_channel(uint32_t client_id, uint32_t channel_ } } -BroadcastStartResult Server::start_broadcast(uint32_t client_id, uint8_t btype, uint32_t track_id) { - auto result = librtc_client_broadcast(this->server_ptr, client_id, btype, track_id); +BroadcastStartResult Server::start_broadcast_audio(RTCClientId client_id, RTCStreamId track_id) { + auto result = librtc_client_broadcast_audio(this->server_ptr, client_id, track_id); + switch(result) { + case 0x00: return BroadcastStartResult::Success; + case 0x01: return BroadcastStartResult::InvalidClient; + case 0x02: return BroadcastStartResult::ClientHasNoChannel; + case 0x03: return BroadcastStartResult::InvalidBroadcastType; + case 0x04: return BroadcastStartResult::InvalidStreamId; + default: + logCritical(LOG_GENERAL, "Audio broadcast start returned unknown error code: {}", result); + return BroadcastStartResult::UnknownError; + } +} + +BroadcastStartResult Server::start_broadcast_video(RTCClientId client_id, VideoBroadcastType broadcast_type, RTCStreamId track_id, const VideoBroadcastOptions* options) { + auto result = librtc_client_broadcast_video(this->server_ptr, client_id, (uint8_t) broadcast_type, track_id, options); switch(result) { case 0x00: return BroadcastStartResult::Success; case 0x01: return BroadcastStartResult::InvalidClient; diff --git a/server/src/rtc/lib.h b/server/src/rtc/lib.h index 1603e56..f31870a 100644 --- a/server/src/rtc/lib.h +++ b/server/src/rtc/lib.h @@ -8,6 +8,8 @@ namespace ts::server { class SpeakingClient; } +struct VideoBroadcastOptions; + namespace ts::rtc { typedef uint32_t RTCClientId; typedef uint32_t RTCChannelId; @@ -45,6 +47,14 @@ namespace ts::rtc { UnknownError }; + enum struct VideoBroadcastConfigureResult { + Success, + InvalidClient, + InvalidBroadcast, /* Client might not be broadcasting */ + InvalidBroadcastType, + UnknownError + }; + class NativeAudioSourceSupplier; class Server { public: @@ -71,14 +81,21 @@ namespace ts::rtc { /* channel actions */ uint32_t create_channel(); ChannelAssignResult assign_channel(RTCClientId /* client id */, RTCChannelId /* channel id */); - BroadcastStartResult start_broadcast(RTCClientId /* client id */, uint8_t /* broadcast type */, RTCStreamId /* stream id */); - VideoBroadcastJoinResult join_video_broadcast(RTCClientId /* client id */, RTCClientId /* target id */, VideoBroadcastType /* broadcast type */); - void leave_video_broadcast(RTCClientId /* client id */, RTCClientId /* target id */, VideoBroadcastType /* broadcast type */); void destroy_channel(RTCChannelId /* channel id */); - /* whisper actions */ + /* Audio */ + BroadcastStartResult start_broadcast_audio(RTCClientId /* client id */, RTCStreamId /* stream id */); + bool configure_whisper_session(std::string& /* error */, RTCClientId /* client id */, uint32_t /* source stream id */, RTCClientId* /* session members */, size_t /* session member count */); void reset_whisper_session(RTCClientId /* client id */); + + /* Video */ + BroadcastStartResult start_broadcast_video(RTCClientId /* client id */, VideoBroadcastType /* broadcast type */, RTCStreamId /* stream id */, const VideoBroadcastOptions* /* options */); + VideoBroadcastConfigureResult client_broadcast_video_configure(RTCClientId /* client id */, VideoBroadcastType /* broadcast type */, const VideoBroadcastOptions* /* options */); + VideoBroadcastConfigureResult client_broadcast_video_config(RTCClientId /* client id */, VideoBroadcastType /* broadcast type */, VideoBroadcastOptions* /* options */); + + VideoBroadcastJoinResult join_video_broadcast(RTCClientId /* client id */, RTCClientId /* target id */, VideoBroadcastType /* broadcast type */); + void leave_video_broadcast(RTCClientId /* client id */, RTCClientId /* target id */, VideoBroadcastType /* broadcast type */); private: void* server_ptr{nullptr}; };