Remove legacy groupCall payload types.

This commit is contained in:
levlam 2021-04-30 17:49:00 +03:00
parent d8b905a986
commit 6e7c53eb74
9 changed files with 68 additions and 560 deletions

View File

@ -2168,54 +2168,12 @@ groupCallRecentSpeaker participant_id:MessageSender is_speaking:Bool = GroupCall
//@duration Call duration; for ended calls only
groupCall id:int32 title:string scheduled_start_date:int32 enabled_start_notification:Bool is_active:Bool is_joined:Bool need_rejoin:Bool can_be_managed:Bool participant_count:int32 loaded_all_participants:Bool recent_speakers:vector<groupCallRecentSpeaker> mute_new_participants:Bool can_change_mute_new_participants:Bool record_duration:int32 duration:int32 = GroupCall;
//@description Describes a payload fingerprint for interaction with tgcalls @hash Value of the field hash @setup Value of the field setup @fingerprint Value of the field fingerprint
groupCallPayloadFingerprint hash:string setup:string fingerprint:string = GroupCallPayloadFingerprint;
//@description Describes a payload for interaction with tgcalls @ufrag Value of the field ufrag @pwd Value of the field pwd @fingerprints The list of fingerprints
groupCallPayload ufrag:string pwd:string fingerprints:vector<groupCallPayloadFingerprint> = GroupCallPayload;
//@description Describes a video payload feedback type for interaction with tgcalls @type Value of the field type @subtype Value of the field subtype
groupCallVideoPayloadFeedbackType type:string subtype:string = GroupCallVideoPayloadFeedbackType;
//@description Describes a video payload parameter for interaction with tgcalls @name Parameter's name @value Parameter's value
groupCallVideoPayloadParameter name:string value:string = GroupCallVideoPayloadParameter;
//@description Describes a video payload type for interaction with tgcalls @id Video payload identifier @name Name of the video payload @clock_rate Video clock rate
//@channel_count Number of channels @feedback_types Video payload feedback types @parameters Video payload parameters
groupCallVideoPayloadType id:int32 name:string clock_rate:int32 channel_count:int32 feedback_types:vector<groupCallVideoPayloadFeedbackType> parameters:vector<groupCallVideoPayloadParameter> = GroupCallVideoPayloadType;
//@description Describes an RTP header extension for interaction with tgcalls @id Extension identifier @name Name of the extension
groupCallVideoExtension id:int32 name:string = GroupCallVideoExtension;
//@description Describes a group of video synchronization sources @sources The list of synchronization sources @semantics The semantics of sources, one of "SIM" or "FID"
groupCallVideoSourceGroup sources:vector<int32> semantics:string = GroupCallVideoSourceGroup;
//@description Describes a video payload for interaction with tgcalls @payload_types List of payload types @extensions List of RTP header extensions @source_groups List of video source groups
groupCallVideoPayload payload_types:vector<groupCallVideoPayloadType> extensions:vector<groupCallVideoExtension> source_groups:vector<groupCallVideoSourceGroup> = GroupCallVideoPayload;
//@description Describes a join response candidate for interaction with tgcalls @port Value of the field port @protocol Value of the field protocol @network Value of the field network
//@generation Value of the field generation @id Value of the field id @component Value of the field component @foundation Value of the field foundation @priority Value of the field priority
//@ip Value of the field ip @type Value of the field type @tcp_type Value of the field tcp_type @rel_addr Value of the field rel_addr @rel_port Value of the field rel_port
groupCallJoinResponseCandidate port:string protocol:string network:string generation:string id:string component:string foundation:string priority:string ip:string type:string tcp_type:string rel_addr:string rel_port:string = GroupCallJoinResponseCandidate;
//@class GroupCallJoinResponse @description Describes a group call join response
//@description Contains data needed to join the group call with WebRTC
//@payload Group call payload to pass to tgcalls
//@candidates Join response candidates to pass to tgcalls
//@server_video_bandwidth_probing_source Join response serverVideoBandwidthProbingSource to pass to tgcalls
groupCallJoinResponseWebrtc payload:groupCallPayload candidates:vector<groupCallJoinResponseCandidate> server_video_bandwidth_probing_source:int32 = GroupCallJoinResponse;
//@description Describes that group call needs to be joined as a stream
groupCallJoinResponseStream = GroupCallJoinResponse;
//@description Represents a group call participant
//@participant_id Identifier of the group call participant
//@audio_source User's audio synchronization source
//@endpoint_id User's endpoint identifier
//@video_payload Description of user's video payload; may be null
//@video_payload Description of user's video payload; may be empty if there is no video channel
//@bio The participant user's bio or the participant chat's description
//@is_current_user True, if the participant is the current user
//@is_speaking True, if the participant is speaking as set by setGroupCallParticipantIsSpeaking
@ -2229,7 +2187,7 @@ groupCallJoinResponseStream = GroupCallJoinResponse;
//@can_unmute_self True, if the participant is muted for all users, but can unmute themself
//@volume_level Participant's volume level; 1-20000 in hundreds of percents
//@order User's order in the group call participant list. Orders must be compared lexicographically. The bigger is order, the higher is user in the list. If order is empty, the user must be removed from the participant list
groupCallParticipant participant_id:MessageSender audio_source:int32 endpoint_id:string video_payload:groupCallVideoPayload bio:string is_current_user:Bool is_speaking:Bool is_hand_raised:Bool can_be_muted_for_all_users:Bool can_be_unmuted_for_all_users:Bool can_be_muted_for_current_user:Bool can_be_unmuted_for_current_user:Bool is_muted_for_all_users:Bool is_muted_for_current_user:Bool can_unmute_self:Bool volume_level:int32 order:string = GroupCallParticipant;
groupCallParticipant participant_id:MessageSender audio_source:int32 endpoint_id:string video_payload:string bio:string is_current_user:Bool is_speaking:Bool is_hand_raised:Bool can_be_muted_for_all_users:Bool can_be_unmuted_for_all_users:Bool can_be_muted_for_current_user:Bool can_be_unmuted_for_current_user:Bool is_muted_for_all_users:Bool is_muted_for_current_user:Bool can_unmute_self:Bool volume_level:int32 order:string = GroupCallParticipant;
//@class CallProblem @description Describes the exact type of a problem with a call
@ -4656,12 +4614,11 @@ toggleGroupCallEnabledStartNotification group_call_id:int32 enabled_start_notifi
//@description Joins an active group call
//@group_call_id Group call identifier
//@participant_id Identifier of a group call participant, which will be used to join the call; voice chats only
//@payload Group join payload; received from tgcalls
//@audio_source Caller audio synchronization source identifier; received from tgcalls
//@video_payload Group join video payload, received from tgcalls. Use null if video is not supported
//@payload Group join payload; received from tgcalls
//@is_muted True, if the user's microphone is muted
//@invite_hash If non-empty, invite hash to be used to join the group call without being muted by administrators
joinGroupCall group_call_id:int32 participant_id:MessageSender payload:groupCallPayload audio_source:int32 video_payload:groupCallVideoPayload is_muted:Bool invite_hash:string = GroupCallJoinResponse;
joinGroupCall group_call_id:int32 participant_id:MessageSender audio_source:int32 payload:string is_muted:Bool invite_hash:string = Text;
//@description Sets group call title. Requires groupCall.can_be_managed group call flag @group_call_id Group call identifier @title New group call title; 1-64 characters
setGroupCallTitle group_call_id:int32 title:string = Ok;

View File

@ -812,7 +812,7 @@ struct GroupCallManager::PendingJoinRequest {
uint64 generation = 0;
int32 audio_source = 0;
DialogId as_dialog_id;
Promise<td_api::object_ptr<td_api::GroupCallJoinResponse>> promise;
Promise<string> promise;
};
GroupCallManager::GroupCallManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
@ -2276,11 +2276,9 @@ void GroupCallManager::start_scheduled_group_call(GroupCallId group_call_id, Pro
td_->create_handler<StartScheduledGroupCallQuery>(std::move(promise))->send(input_group_call_id);
}
void GroupCallManager::join_group_call(GroupCallId group_call_id, DialogId as_dialog_id,
td_api::object_ptr<td_api::groupCallPayload> &&payload, int32 audio_source,
td_api::object_ptr<td_api::groupCallVideoPayload> &&video_payload, bool is_muted,
const string &invite_hash,
Promise<td_api::object_ptr<td_api::GroupCallJoinResponse>> &&promise) {
void GroupCallManager::join_group_call(GroupCallId group_call_id, DialogId as_dialog_id, int32 audio_source,
const string &payload, bool is_muted, const string &invite_hash,
Promise<string> &&promise) {
TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id));
auto *group_call = get_group_call(input_group_call_id);
@ -2324,9 +2322,6 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, DialogId as_di
}
}
TRY_RESULT_PROMISE(promise, json_payload,
encode_join_group_call_payload(std::move(payload), audio_source, std::move(video_payload)));
if (group_call->is_being_left) {
group_call->is_being_left = false;
need_update |= group_call->is_joined;
@ -2347,7 +2342,7 @@ void GroupCallManager::join_group_call(GroupCallId group_call_id, DialogId as_di
result.move_as_error());
});
request->query_ref = td_->create_handler<JoinGroupCallQuery>(std::move(query_promise))
->send(input_group_call_id, as_dialog_id, json_payload, is_muted, invite_hash, generation);
->send(input_group_call_id, as_dialog_id, payload, is_muted, invite_hash, generation);
if (group_call->dialog_id.is_valid()) {
td_->messages_manager_->on_update_dialog_default_join_group_call_as_dialog_id(group_call->dialog_id, as_dialog_id,
@ -2465,31 +2460,23 @@ bool GroupCallManager::on_join_group_call_response(InputGroupCallId input_group_
}
CHECK(it->second != nullptr);
auto result = get_group_call_join_response_object(std::move(json_response));
bool need_update = false;
if (result.is_error()) {
LOG(ERROR) << "Failed to parse join response JSON object: " << result.error().message();
it->second->promise.set_error(Status::Error(500, "Receive invalid join group call response payload"));
} else {
auto group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr);
group_call->is_joined = true;
group_call->need_rejoin = false;
group_call->is_being_left = false;
group_call->joined_date = G()->unix_time();
group_call->audio_source = it->second->audio_source;
group_call->as_dialog_id = it->second->as_dialog_id;
it->second->promise.set_value(result.move_as_ok());
if (group_call->audio_source != 0) {
check_group_call_is_joined_timeout_.set_timeout_in(group_call->group_call_id.get(),
CHECK_GROUP_CALL_IS_JOINED_TIMEOUT);
}
need_update = true;
auto group_call = get_group_call(input_group_call_id);
CHECK(group_call != nullptr);
group_call->is_joined = true;
group_call->need_rejoin = false;
group_call->is_being_left = false;
group_call->joined_date = G()->unix_time();
group_call->audio_source = it->second->audio_source;
group_call->as_dialog_id = it->second->as_dialog_id;
it->second->promise.set_value(std::move(json_response));
if (group_call->audio_source != 0) {
check_group_call_is_joined_timeout_.set_timeout_in(group_call->group_call_id.get(),
CHECK_GROUP_CALL_IS_JOINED_TIMEOUT);
}
pending_join_requests_.erase(it);
need_update |= try_clear_group_call_participants(input_group_call_id);
try_clear_group_call_participants(input_group_call_id);
process_group_call_after_join_requests(input_group_call_id, "on_join_group_call_response");
return need_update;
return true;
}
void GroupCallManager::finish_join_group_call(InputGroupCallId input_group_call_id, uint64 generation, Status error) {

View File

@ -64,10 +64,8 @@ class GroupCallManager : public Actor {
void start_scheduled_group_call(GroupCallId group_call_id, Promise<Unit> &&promise);
void join_group_call(GroupCallId group_call_id, DialogId as_dialog_id,
td_api::object_ptr<td_api::groupCallPayload> &&payload, int32 audio_source,
td_api::object_ptr<td_api::groupCallVideoPayload> &&video_payload, bool is_muted,
const string &invite_hash, Promise<td_api::object_ptr<td_api::GroupCallJoinResponse>> &&promise);
void join_group_call(GroupCallId group_call_id, DialogId as_dialog_id, int32 audio_source, const string &payload,
bool is_muted, const string &invite_hash, Promise<string> &&promise);
void set_group_call_title(GroupCallId group_call_id, string title, Promise<Unit> &&promise);

View File

@ -57,12 +57,8 @@ GroupCallParticipant::GroupCallParticipant(const tl_object_ptr<telegram_api::gro
version = call_version;
if (participant->video_ != nullptr) {
auto r_video_payload = get_group_call_video_payload(participant->video_->data_, endpoint);
if (r_video_payload.is_error()) {
LOG(ERROR) << "Failed to parse GroupCallParticipant params: " << r_video_payload.error();
} else {
video_payload = r_video_payload.move_as_ok();
}
get_group_call_video_payload(participant->video_->data_, endpoint);
video_payload = std::move(participant->video_->data_);
}
}
@ -256,11 +252,10 @@ td_api::object_ptr<td_api::groupCallParticipant> GroupCallParticipant::get_group
}
return td_api::make_object<td_api::groupCallParticipant>(
td->messages_manager_->get_message_sender_object(dialog_id), audio_source, endpoint,
get_group_call_video_payload_object(video_payload), about, is_self, is_speaking, get_is_hand_raised(),
can_be_muted_for_all_users, can_be_unmuted_for_all_users, can_be_muted_only_for_self,
can_be_unmuted_only_for_self, get_is_muted_for_all_users(), get_is_muted_locally(), get_is_muted_by_themselves(),
get_volume_level(), order.get_group_call_participant_order_object());
td->messages_manager_->get_message_sender_object(dialog_id), audio_source, endpoint, video_payload, about,
is_self, is_speaking, get_is_hand_raised(), can_be_muted_for_all_users, can_be_unmuted_for_all_users,
can_be_muted_only_for_self, can_be_unmuted_only_for_self, get_is_muted_for_all_users(), get_is_muted_locally(),
get_is_muted_by_themselves(), get_volume_level(), order.get_group_call_participant_order_object());
}
bool operator==(const GroupCallParticipant &lhs, const GroupCallParticipant &rhs) {

View File

@ -22,7 +22,7 @@ class Td;
struct GroupCallParticipant {
DialogId dialog_id;
string about;
GroupCallVideoPayload video_payload;
string video_payload;
string endpoint;
int32 audio_source = 0;
int32 joined_date = 0;

View File

@ -6,402 +6,26 @@
//
#include "td/telegram/GroupCallVideoPayload.h"
#include "td/telegram/misc.h"
#include "td/utils/algorithm.h"
#include "td/utils/JsonBuilder.h"
#include "td/utils/misc.h"
namespace td {
static bool operator==(const GroupCallVideoPayloadFeedbackType &lhs, const GroupCallVideoPayloadFeedbackType &rhs) {
return lhs.type == rhs.type && lhs.subtype == rhs.subtype;
}
static td_api::object_ptr<td_api::groupCallVideoPayloadFeedbackType> get_group_call_video_payload_feedback_type_object(
const GroupCallVideoPayloadFeedbackType &feedback_type) {
return td_api::make_object<td_api::groupCallVideoPayloadFeedbackType>(feedback_type.type, feedback_type.subtype);
}
static bool operator==(const GroupCallVideoPayloadParameter &lhs, const GroupCallVideoPayloadParameter &rhs) {
return lhs.name == rhs.name && lhs.value == rhs.value;
}
static td_api::object_ptr<td_api::groupCallVideoPayloadParameter> get_group_call_video_payload_parameter_object(
const GroupCallVideoPayloadParameter &parameter) {
return td_api::make_object<td_api::groupCallVideoPayloadParameter>(parameter.name, parameter.value);
}
static bool operator==(const GroupCallVideoPayloadType &lhs, const GroupCallVideoPayloadType &rhs) {
return lhs.id == rhs.id && lhs.name == rhs.name && lhs.clock_rate == rhs.clock_rate &&
lhs.channel_count == rhs.channel_count && lhs.feedback_types == rhs.feedback_types &&
lhs.parameters == rhs.parameters;
}
static td_api::object_ptr<td_api::groupCallVideoPayloadType> get_group_call_video_payload_type_object(
const GroupCallVideoPayloadType &payload_type) {
return td_api::make_object<td_api::groupCallVideoPayloadType>(
payload_type.id, payload_type.name, payload_type.clock_rate, payload_type.channel_count,
transform(payload_type.feedback_types, get_group_call_video_payload_feedback_type_object),
transform(payload_type.parameters, get_group_call_video_payload_parameter_object));
}
static bool operator==(const GroupCallVideoExtension &lhs, const GroupCallVideoExtension &rhs) {
return lhs.id == rhs.id && lhs.name == rhs.name;
}
static td_api::object_ptr<td_api::groupCallVideoExtension> get_group_call_video_extension_object(
const GroupCallVideoExtension &extension) {
return td_api::make_object<td_api::groupCallVideoExtension>(extension.id, extension.name);
}
static bool operator==(const GroupCallVideoSourceGroup &lhs, const GroupCallVideoSourceGroup &rhs) {
return lhs.sources == rhs.sources && lhs.semantics == rhs.semantics;
}
static td_api::object_ptr<td_api::groupCallVideoSourceGroup> get_group_call_video_source_group_object(
const GroupCallVideoSourceGroup &source_group) {
return td_api::make_object<td_api::groupCallVideoSourceGroup>(vector<int32>(source_group.sources),
source_group.semantics);
}
bool operator==(const GroupCallVideoPayload &lhs, const GroupCallVideoPayload &rhs) {
return lhs.payload_types == rhs.payload_types && lhs.extensions == rhs.extensions &&
lhs.source_groups == rhs.source_groups;
}
bool operator!=(const GroupCallVideoPayload &lhs, const GroupCallVideoPayload &rhs) {
return !(lhs == rhs);
}
td_api::object_ptr<td_api::groupCallVideoPayload> get_group_call_video_payload_object(
const GroupCallVideoPayload &payload) {
if (payload.payload_types.empty() && payload.extensions.empty() && payload.source_groups.empty()) {
return nullptr;
}
return td_api::make_object<td_api::groupCallVideoPayload>(
transform(payload.payload_types, get_group_call_video_payload_type_object),
transform(payload.extensions, get_group_call_video_extension_object),
transform(payload.source_groups, get_group_call_video_source_group_object));
}
static Result<GroupCallVideoPayloadType> get_group_call_video_payload_type(JsonValue &&value) {
if (value.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object as payload type");
}
GroupCallVideoPayloadType result;
auto &value_object = value.get_object();
TRY_RESULT_ASSIGN(result.id, get_json_object_int_field(value_object, "id", false));
TRY_RESULT_ASSIGN(result.name, get_json_object_string_field(value_object, "name", false));
TRY_RESULT_ASSIGN(result.clock_rate, get_json_object_int_field(value_object, "clockrate", false));
TRY_RESULT_ASSIGN(result.channel_count, get_json_object_int_field(value_object, "channels"));
TRY_RESULT(feedback_types, get_json_object_field(value_object, "rtcp-fbs", JsonValue::Type::Array));
TRY_RESULT(parameters, get_json_object_field(value_object, "parameters", JsonValue::Type::Object));
if (feedback_types.type() != JsonValue::Type::Null) {
CHECK(feedback_types.type() == JsonValue::Type::Array);
for (auto &feedback_type_value : feedback_types.get_array()) {
if (feedback_type_value.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object as feedback type");
}
auto &feedback_type_object = feedback_type_value.get_object();
GroupCallVideoPayloadFeedbackType feedback_type;
TRY_RESULT_ASSIGN(feedback_type.type, get_json_object_string_field(feedback_type_object, "type", false));
TRY_RESULT_ASSIGN(feedback_type.subtype, get_json_object_string_field(feedback_type_object, "subtype"));
result.feedback_types.push_back(std::move(feedback_type));
}
}
if (parameters.type() != JsonValue::Type::Null) {
CHECK(parameters.type() == JsonValue::Type::Object);
for (auto &parameter_value : parameters.get_object()) {
GroupCallVideoPayloadParameter parameter;
parameter.name = parameter_value.first.str();
if (parameter_value.second.type() == JsonValue::Type::String) {
parameter.value = parameter_value.second.get_string().str();
} else if (parameter_value.second.type() == JsonValue::Type::Number) {
parameter.value = parameter_value.second.get_number().str();
} else {
return Status::Error("Receive unexpected parameter type");
}
result.parameters.push_back(std::move(parameter));
}
}
return result;
}
static Result<GroupCallVideoExtension> get_group_call_video_extension(JsonValue &&value) {
if (value.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object as RTP header extension");
}
GroupCallVideoExtension result;
auto &value_object = value.get_object();
TRY_RESULT_ASSIGN(result.id, get_json_object_int_field(value_object, "id", false));
TRY_RESULT_ASSIGN(result.name, get_json_object_string_field(value_object, "uri", false));
return result;
}
static Result<GroupCallVideoSourceGroup> get_group_call_video_source_group(JsonValue &&value) {
if (value.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object as synchronization source group");
}
GroupCallVideoSourceGroup result;
auto &value_object = value.get_object();
TRY_RESULT(sources, get_json_object_field(value_object, "sources", JsonValue::Type::Array, false));
TRY_RESULT_ASSIGN(result.semantics, get_json_object_string_field(value_object, "semantics", false));
for (auto &source : sources.get_array()) {
Slice source_str;
if (source.type() == JsonValue::Type::String) {
source_str = source.get_string();
} else if (source.type() == JsonValue::Type::Number) {
source_str = source.get_number();
}
TRY_RESULT(source_id, to_integer_safe<int32>(source_str));
result.sources.push_back(source_id);
}
return result;
}
Result<GroupCallVideoPayload> get_group_call_video_payload(string json, string &endpoint) {
void get_group_call_video_payload(string json, string &endpoint) {
auto r_value = json_decode(json);
if (r_value.is_error()) {
return Status::Error("Can't parse JSON object");
return;
}
auto value = r_value.move_as_ok();
if (value.type() != JsonValue::Type::Object) {
return Status::Error("Expected an Object");
return;
}
auto &value_object = value.get_object();
TRY_RESULT_ASSIGN(endpoint, get_json_object_string_field(value_object, "endpoint", false));
TRY_RESULT(source_groups, get_json_object_field(value_object, "ssrc-groups", JsonValue::Type::Array, false));
GroupCallVideoPayload result;
for (auto &source_group_object : source_groups.get_array()) {
TRY_RESULT(source_group, get_group_call_video_source_group(std::move(source_group_object)));
result.source_groups.push_back(std::move(source_group));
auto r_endpoint = get_json_object_string_field(value_object, "endpoint", true);
if (r_endpoint.is_ok()) {
endpoint = r_endpoint.move_as_ok();
}
return result;
}
Result<string> encode_join_group_call_payload(td_api::object_ptr<td_api::groupCallPayload> &&payload,
int32 audio_source,
td_api::object_ptr<td_api::groupCallVideoPayload> &&video_payload) {
if (payload == nullptr) {
return Status::Error(400, "Payload must be non-empty");
}
if (!clean_input_string(payload->ufrag_)) {
return Status::Error(400, "Payload ufrag must be encoded in UTF-8");
}
if (!clean_input_string(payload->pwd_)) {
return Status::Error(400, "Payload pwd must be encoded in UTF-8");
}
for (auto &fingerprint : payload->fingerprints_) {
if (fingerprint == nullptr) {
return Status::Error(400, "Payload fingerprint must be non-empty");
}
if (!clean_input_string(fingerprint->hash_)) {
return Status::Error(400, "Fingerprint hash must be encoded in UTF-8");
}
if (!clean_input_string(fingerprint->setup_)) {
return Status::Error(400, "Fingerprint setup must be encoded in UTF-8");
}
if (!clean_input_string(fingerprint->fingerprint_)) {
return Status::Error(400, "Fingerprint must be encoded in UTF-8");
}
}
if (audio_source == 0) {
return Status::Error(400, "Audio synchronization source must be non-zero");
}
if (video_payload != nullptr) {
for (auto &payload_type : video_payload->payload_types_) {
if (!clean_input_string(payload_type->name_)) {
return Status::Error(400, "Video payload type name must be encoded in UTF-8");
}
for (auto &feedback_type : payload_type->feedback_types_) {
if (!clean_input_string(feedback_type->type_)) {
return Status::Error(400, "Video feedback type must be encoded in UTF-8");
}
if (!clean_input_string(feedback_type->subtype_)) {
return Status::Error(400, "Video feedback subtype must be encoded in UTF-8");
}
}
for (auto &parameter : payload_type->parameters_) {
if (!clean_input_string(parameter->name_)) {
return Status::Error(400, "Video parameter name must be encoded in UTF-8");
}
if (!clean_input_string(parameter->value_)) {
return Status::Error(400, "Video parameter value must be encoded in UTF-8");
}
}
}
for (auto &extension : video_payload->extensions_) {
if (!clean_input_string(extension->name_)) {
return Status::Error(400, "RTP header extension name must be encoded in UTF-8");
}
}
for (auto &source_group : video_payload->source_groups_) {
if (!clean_input_string(source_group->semantics_)) {
return Status::Error(400, "Video source group semantics must be encoded in UTF-8");
}
}
}
return json_encode<string>(json_object([&payload, audio_source, &video_payload](auto &o) {
o("ufrag", payload->ufrag_);
o("pwd", payload->pwd_);
o("fingerprints", json_array(payload->fingerprints_,
[](const td_api::object_ptr<td_api::groupCallPayloadFingerprint> &fingerprint) {
return json_object([&fingerprint](auto &o) {
o("hash", fingerprint->hash_);
o("setup", fingerprint->setup_);
o("fingerprint", fingerprint->fingerprint_);
});
}));
o("ssrc", audio_source);
if (video_payload != nullptr) {
o("payload-types",
json_array(video_payload->payload_types_,
[](const td_api::object_ptr<td_api::groupCallVideoPayloadType> &payload_type) {
return json_object([&payload_type](auto &o) {
o("id", payload_type->id_);
o("name", payload_type->name_);
o("clockrate", payload_type->clock_rate_);
o("channels", payload_type->channel_count_);
if (!payload_type->feedback_types_.empty()) {
o("rtcp-fbs",
json_array(
payload_type->feedback_types_,
[](const td_api::object_ptr<td_api::groupCallVideoPayloadFeedbackType> &feedback_type) {
return json_object([&feedback_type](auto &o) {
o("type", feedback_type->type_);
if (!feedback_type->subtype_.empty()) {
o("subtype", feedback_type->subtype_);
}
});
}));
}
if (!payload_type->parameters_.empty()) {
o("parameters", json_object([parameters = &payload_type->parameters_](auto &o) {
for (auto &parameter : *parameters) {
o(parameter->name_, parameter->value_);
}
}));
}
});
}));
o("rtp-hdrexts", json_array(video_payload->extensions_,
[](const td_api::object_ptr<td_api::groupCallVideoExtension> &extension) {
return json_object([&extension](auto &o) {
o("id", extension->id_);
o("uri", extension->name_);
});
}));
o("ssrc-groups", json_array(video_payload->source_groups_,
[](const td_api::object_ptr<td_api::groupCallVideoSourceGroup> &source_group) {
return json_object([&source_group](auto &o) {
o("sources",
json_array(source_group->sources_, [](int32 source) { return source; }));
o("semantics", source_group->semantics_);
});
}));
}
}));
}
Result<td_api::object_ptr<td_api::GroupCallJoinResponse>> get_group_call_join_response_object(string json) {
auto r_value = json_decode(json);
if (r_value.is_error()) {
return Status::Error("Can't parse JSON object");
}
auto value = r_value.move_as_ok();
if (value.type() != JsonValue::Type::Object) {
return Status::Error("Expected an Object");
}
auto &value_object = value.get_object();
auto r_stream = get_json_object_bool_field(value_object, "stream");
if (r_stream.is_ok() && r_stream.ok() == true) {
return td_api::make_object<td_api::groupCallJoinResponseStream>();
}
TRY_RESULT(transport, get_json_object_field(value_object, "transport", JsonValue::Type::Object, false));
CHECK(transport.type() == JsonValue::Type::Object);
auto &transport_object = transport.get_object();
TRY_RESULT(candidates, get_json_object_field(transport_object, "candidates", JsonValue::Type::Array, false));
TRY_RESULT(fingerprints, get_json_object_field(transport_object, "fingerprints", JsonValue::Type::Array, false));
TRY_RESULT(ufrag, get_json_object_string_field(transport_object, "ufrag", false));
TRY_RESULT(pwd, get_json_object_string_field(transport_object, "pwd", false));
// skip "xmlns", "rtcp-mux"
vector<td_api::object_ptr<td_api::groupCallPayloadFingerprint>> fingerprints_object;
for (auto &fingerprint : fingerprints.get_array()) {
if (fingerprint.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object as fingerprint");
}
auto &fingerprint_object = fingerprint.get_object();
TRY_RESULT(hash, get_json_object_string_field(fingerprint_object, "hash", false));
TRY_RESULT(setup, get_json_object_string_field(fingerprint_object, "setup", false));
TRY_RESULT(fingerprint_value, get_json_object_string_field(fingerprint_object, "fingerprint", false));
fingerprints_object.push_back(
td_api::make_object<td_api::groupCallPayloadFingerprint>(hash, setup, fingerprint_value));
}
vector<td_api::object_ptr<td_api::groupCallJoinResponseCandidate>> candidates_object;
for (auto &candidate : candidates.get_array()) {
if (candidate.type() != JsonValue::Type::Object) {
return Status::Error("Expected JSON object as candidate");
}
auto &candidate_object = candidate.get_object();
TRY_RESULT(port, get_json_object_string_field(candidate_object, "port", false));
TRY_RESULT(protocol, get_json_object_string_field(candidate_object, "protocol", false));
TRY_RESULT(network, get_json_object_string_field(candidate_object, "network", false));
TRY_RESULT(generation, get_json_object_string_field(candidate_object, "generation", false));
TRY_RESULT(id, get_json_object_string_field(candidate_object, "id", false));
TRY_RESULT(component, get_json_object_string_field(candidate_object, "component", false));
TRY_RESULT(foundation, get_json_object_string_field(candidate_object, "foundation", false));
TRY_RESULT(priority, get_json_object_string_field(candidate_object, "priority", false));
TRY_RESULT(ip, get_json_object_string_field(candidate_object, "ip", false));
TRY_RESULT(type, get_json_object_string_field(candidate_object, "type", false));
TRY_RESULT(tcp_type, get_json_object_string_field(candidate_object, "tcptype"));
TRY_RESULT(rel_addr, get_json_object_string_field(candidate_object, "rel-addr"));
TRY_RESULT(rel_port, get_json_object_string_field(candidate_object, "rel-port"));
candidates_object.push_back(td_api::make_object<td_api::groupCallJoinResponseCandidate>(
port, protocol, network, generation, id, component, foundation, priority, ip, type, tcp_type, rel_addr,
rel_port));
}
TRY_RESULT(video, get_json_object_field(value_object, "video", JsonValue::Type::Object, true));
int32 server_video_bandwidth_probing_source = 0;
if (video.type() == JsonValue::Type::Object) {
auto &video_object = video.get_object();
TRY_RESULT(server_sources, get_json_object_field(video_object, "server_sources", JsonValue::Type::Array, false));
auto &server_sources_array = server_sources.get_array();
if (server_sources_array.empty()) {
return Status::Error("Expected at least one server source");
}
if (server_sources_array[0].type() != JsonValue::Type::Number) {
return Status::Error("Expected Number as server source");
}
TRY_RESULT_ASSIGN(server_video_bandwidth_probing_source,
to_integer_safe<int32>(server_sources_array[0].get_number()));
}
auto payload = td_api::make_object<td_api::groupCallPayload>(ufrag, pwd, std::move(fingerprints_object));
return td_api::make_object<td_api::groupCallJoinResponseWebrtc>(std::move(payload), std::move(candidates_object),
server_video_bandwidth_probing_source);
}
} // namespace td

View File

@ -6,62 +6,10 @@
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
struct GroupCallVideoPayloadFeedbackType {
string type;
string subtype;
};
struct GroupCallVideoPayloadParameter {
string name;
string value;
};
struct GroupCallVideoPayloadType {
int32 id;
string name;
int32 clock_rate;
int32 channel_count;
vector<GroupCallVideoPayloadFeedbackType> feedback_types;
vector<GroupCallVideoPayloadParameter> parameters;
};
struct GroupCallVideoExtension {
int32 id;
string name;
};
struct GroupCallVideoSourceGroup {
vector<int32> sources;
string semantics;
};
struct GroupCallVideoPayload {
vector<GroupCallVideoPayloadType> payload_types;
vector<GroupCallVideoExtension> extensions;
vector<GroupCallVideoSourceGroup> source_groups;
};
bool operator==(const GroupCallVideoPayload &lhs, const GroupCallVideoPayload &rhs);
bool operator!=(const GroupCallVideoPayload &lhs, const GroupCallVideoPayload &rhs);
td_api::object_ptr<td_api::groupCallVideoPayload> get_group_call_video_payload_object(
const GroupCallVideoPayload &payload);
Result<GroupCallVideoPayload> get_group_call_video_payload(string json, string &endpoint);
Result<string> encode_join_group_call_payload(td_api::object_ptr<td_api::groupCallPayload> &&payload,
int32 audio_source,
td_api::object_ptr<td_api::groupCallVideoPayload> &&video_payload);
Result<td_api::object_ptr<td_api::GroupCallJoinResponse>> get_group_call_join_response_object(string json);
void get_group_call_video_payload(string json, string &endpoint);
} // namespace td

View File

@ -6020,11 +6020,19 @@ void Td::on_request(uint64 id, const td_api::toggleGroupCallEnabledStartNotifica
void Td::on_request(uint64 id, td_api::joinGroupCall &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.invite_hash_);
CLEAN_INPUT_STRING(request.payload_);
CREATE_REQUEST_PROMISE();
group_call_manager_->join_group_call(
GroupCallId(request.group_call_id_), group_call_manager_->get_group_call_participant_id(request.participant_id_),
std::move(request.payload_), request.audio_source_, std::move(request.video_payload_), request.is_muted_,
request.invite_hash_, std::move(promise));
auto query_promise = PromiseCreator::lambda([promise = std::move(promise)](Result<string> result) mutable {
if (result.is_error()) {
promise.set_error(result.move_as_error());
} else {
promise.set_value(make_tl_object<td_api::text>(result.move_as_ok()));
}
});
group_call_manager_->join_group_call(GroupCallId(request.group_call_id_),
group_call_manager_->get_group_call_participant_id(request.participant_id_),
request.audio_source_, std::move(request.payload_), request.is_muted_,
request.invite_hash_, std::move(query_promise));
}
void Td::on_request(uint64 id, td_api::setGroupCallTitle &request) {

View File

@ -2696,33 +2696,24 @@ class CliClient final : public Actor {
string participant_id;
string invite_hash;
get_args(args, group_call_id, participant_id, invite_hash);
vector<td_api::object_ptr<td_api::groupCallPayloadFingerprint>> fingerprints;
fingerprints.push_back(td_api::make_object<td_api::groupCallPayloadFingerprint>("hash", "setup", "fingerprint"));
fingerprints.push_back(td_api::make_object<td_api::groupCallPayloadFingerprint>("h2", "s2", "fingerprint2"));
vector<td_api::object_ptr<td_api::groupCallVideoPayloadFeedbackType>> feedback_types;
feedback_types.push_back(
td_api::make_object<td_api::groupCallVideoPayloadFeedbackType>("transport-cc", "subtype1"));
feedback_types.push_back(td_api::make_object<td_api::groupCallVideoPayloadFeedbackType>("type2", "subtype2"));
vector<td_api::object_ptr<td_api::groupCallVideoPayloadParameter>> parameters;
parameters.push_back(td_api::make_object<td_api::groupCallVideoPayloadParameter>("minptime", "10"));
parameters.push_back(td_api::make_object<td_api::groupCallVideoPayloadParameter>("useinbandfec", "1"));
auto video_payload = td_api::make_object<td_api::groupCallVideoPayload>();
video_payload->payload_types_.push_back(td_api::make_object<td_api::groupCallVideoPayloadType>(
12345, "opus", 48000, 2, std::move(feedback_types), std::move(parameters)));
video_payload->extensions_.push_back(
td_api::make_object<td_api::groupCallVideoExtension>(1, "urn:ietf:params:rtp-hdrext:ssrc-audio-level"));
video_payload->source_groups_.push_back(
td_api::make_object<td_api::groupCallVideoSourceGroup>(vector<int32>{1, 2}, "SIM"));
video_payload->source_groups_.push_back(
td_api::make_object<td_api::groupCallVideoSourceGroup>(vector<int32>{3, 4}, "FID"));
send_request(td_api::make_object<td_api::joinGroupCall>(
as_group_call_id(group_call_id), as_message_sender(participant_id),
td_api::make_object<td_api::groupCallPayload>("ufrag", "pwd", std::move(fingerprints)), group_call_source_,
op == "jgc" ? nullptr : std::move(video_payload), true, invite_hash));
auto payload = PSTRING() << "{\"ufrag\":\"ufrag\",\"pwd\":\"pwd\",\"fingerprints\":[{\"hash\":\"hash\",\"setup\":"
"\"setup\",\"fingerprint\":\"fingerprint\"},{\"hash\":\"h2\",\"setup\":\"s2\","
"\"fingerprint\":\"fingerprint2\"}],\"ssrc\":"
<< group_call_source_ << ',';
if (op == "jgc") {
payload.back() = '}';
} else {
payload +=
"\"payload-types\":[{\"id\":12345,\"name\":\"opus\",\"clockrate\":48000,\"channels\":2,\"rtcp-fbs\":[{"
"\"type\":\"transport-cc\",\"subtype\":\"subtype1\"},{\"type\":\"type2\",\"subtype\":\"subtype2\"}],"
"\"parameters\":{\"minptime\":\"10\",\"useinbandfec\":\"1\"}}],\"rtp-hdrexts\":[{\"id\":1,\"uri\":\"urn:"
"ietf:params:rtp-hdrext:ssrc-audio-level\"}],\"ssrc-groups\":[{\"sources\":[1,2],\"semantics\":\"SIM\"},{"
"\"sources\":[3,4],\"semantics\":\"FID\"}]}";
}
send_request(td_api::make_object<td_api::joinGroupCall>(as_group_call_id(group_call_id),
as_message_sender(participant_id), group_call_source_,
std::move(payload), true, invite_hash));
} else if (op == "sgct") {
string chat_id;
string title;