Added support for controlling the streams max bps

This commit is contained in:
WolverinDEV
2021-01-04 20:32:29 +01:00
parent a233915064
commit 8be83fc51d
11 changed files with 357 additions and 60 deletions
+255 -44
View File
@@ -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>() : (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>() : (uint32_t) 0;
auto type = (rtc::VideoBroadcastType) command["type"].as<uint8_t>();
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<std::tuple<uint8_t, uint32_t>> 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<uint8_t>();
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<uint8_t>();
VideoBroadcastOptions current_options;
auto query_result = this->server->rtc_server().client_broadcast_video_config(this->rtc_client_id, broadcast_type, &current_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);
}