Added support for controlling the streams max bps
This commit is contained in:
@@ -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, ¤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);
|
||||
}
|
||||
Reference in New Issue
Block a user