Update API scheme to layer 129.

This commit is contained in:
John Preston 2021-04-29 13:56:09 +04:00
parent b3fcb4ef36
commit 69ceed5bbc
6 changed files with 159 additions and 105 deletions

View file

@ -375,6 +375,7 @@ updatePeerHistoryTTL#bb9bb9a5 flags:# peer:Peer ttl_period:flags.0?int = Update;
updateChatParticipant#f3b3781f flags:# chat_id:int date:int actor_id:int user_id:int prev_participant:flags.0?ChatParticipant new_participant:flags.1?ChatParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
updateChannelParticipant#7fecb1ec flags:# channel_id:int date:int actor_id:int user_id:int prev_participant:flags.0?ChannelParticipant new_participant:flags.1?ChannelParticipant invite:flags.2?ExportedChatInvite qts:int = Update;
updateBotStopped#7f9488a user_id:int date:int stopped:Bool qts:int = Update;
updateGroupCallConnection#b783982 flags:# presentation:flags.0?true params:DataJSON = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -1204,11 +1205,11 @@ peerBlocked#e8fd8014 peer_id:Peer date:int = PeerBlocked;
stats.messageStats#8999f295 views_graph:StatsGraph = stats.MessageStats;
groupCallDiscarded#7780bcb4 id:long access_hash:long duration:int = GroupCall;
groupCall#c95c6654 flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true id:long access_hash:long participants_count:int params:flags.0?DataJSON title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int version:int = GroupCall;
groupCall#653dbaad flags:# join_muted:flags.1?true can_change_join_muted:flags.2?true join_date_asc:flags.6?true schedule_start_subscribed:flags.8?true can_start_video:flags.9?true id:long access_hash:long participants_count:int title:flags.3?string stream_dc_id:flags.4?int record_start_date:flags.5?int schedule_date:flags.7?int version:int = GroupCall;
inputGroupCall#d8aa840f id:long access_hash:long = InputGroupCall;
groupCallParticipant#b96b25ee flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true video_muted:flags.14?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long params:flags.6?DataJSON = GroupCallParticipant;
groupCallParticipant#a8ba51a7 flags:# muted:flags.0?true left:flags.1?true can_self_unmute:flags.2?true just_joined:flags.4?true versioned:flags.5?true min:flags.8?true muted_by_you:flags.9?true volume_by_admin:flags.10?true self:flags.12?true peer:Peer date:int active_date:flags.3?int source:int volume:flags.7?int about:flags.11?string raise_hand_rating:flags.13?long video:flags.6?DataJSON presentation:flags.14?DataJSON = GroupCallParticipant;
phone.groupCall#9e727aad call:GroupCall participants:Vector<GroupCallParticipant> participants_next_offset:string chats:Vector<Chat> users:Vector<User> = phone.GroupCall;
@ -1624,7 +1625,7 @@ phone.discardGroupCall#7a777135 call:InputGroupCall = Updates;
phone.toggleGroupCallSettings#74bbb43d flags:# reset_invite_hash:flags.1?true call:InputGroupCall join_muted:flags.0?Bool = Updates;
phone.getGroupCall#c7cb017 call:InputGroupCall = phone.GroupCall;
phone.getGroupParticipants#c558d8ab call:InputGroupCall ids:Vector<InputPeer> sources:Vector<int> offset:string limit:int = phone.GroupParticipants;
phone.checkGroupCall#b74a7bea call:InputGroupCall source:int = Bool;
phone.checkGroupCall#b59cf977 call:InputGroupCall sources:Vector<int> = Vector<int>;
phone.toggleGroupCallRecord#c02a66d7 flags:# start:flags.0?true call:InputGroupCall title:flags.1?string = Updates;
phone.editGroupCallParticipant#aec610e4 flags:# call:InputGroupCall participant:InputPeer muted:flags.0?Bool volume:flags.1?int raise_hand:flags.2?Bool video_muted:flags.3?Bool = Updates;
phone.editGroupCallTitle#1ca6ac0a call:InputGroupCall title:string = Updates;
@ -1633,6 +1634,8 @@ phone.exportGroupCallInvite#e6aa647f flags:# can_self_unmute:flags.0?true call:I
phone.toggleGroupCallStartSubscription#219c34e6 call:InputGroupCall subscribed:Bool = Updates;
phone.startScheduledGroupCall#5680e342 call:InputGroupCall = Updates;
phone.saveDefaultGroupCallJoinAs#575e1f8c peer:InputPeer join_as:InputPeer = Bool;
phone.joinGroupCallPresentation#cbea6bc4 call:InputGroupCall params:DataJSON = Updates;
phone.leaveGroupCallPresentation#1c50d144 call:InputGroupCall = Updates;
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
@ -1649,4 +1652,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
// LAYER 128
// LAYER 129

View file

@ -1878,6 +1878,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updatePhoneCall:
case mtpc_updatePhoneCallSignalingData:
case mtpc_updateGroupCallParticipants:
case mtpc_updateGroupCallConnection:
case mtpc_updateGroupCall: {
Core::App().calls().handleUpdate(&session(), update);
} break;

View file

@ -391,6 +391,8 @@ void Instance::handleUpdate(
handleSignalingData(session, data);
}, [&](const MTPDupdateGroupCall &data) {
handleGroupCallUpdate(session, update);
}, [&](const MTPDupdateGroupCallConnection &data) {
handleGroupCallUpdate(session, update);
}, [&](const MTPDupdateGroupCallParticipants &data) {
handleGroupCallUpdate(session, update);
}, [](const auto &) {
@ -482,10 +484,15 @@ void Instance::handleGroupCallUpdate(
&& (&_currentGroupCall->peer()->session() == session)) {
update.match([&](const MTPDupdateGroupCall &data) {
_currentGroupCall->handlePossibleCreateOrJoinResponse(data);
}, [&](const MTPDupdateGroupCallConnection &data) {
_currentGroupCall->handlePossibleCreateOrJoinResponse(data);
}, [](const auto &) {
});
}
if (update.type() == mtpc_updateGroupCallConnection) {
return;
}
const auto callId = update.match([](const MTPDupdateGroupCall &data) {
return data.vcall().match([](const auto &data) {
return data.vid().v;

View file

@ -76,6 +76,12 @@ constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000);
return msgId / double(1ULL << 32);
}
[[nodiscard]] std::string ReadJsonString(
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
} // namespace
struct VideoParams {
@ -177,13 +183,8 @@ std::shared_ptr<VideoParams> ParseVideoParams(
return data;
}
const auto readString = [](
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
const auto object = document.object();
data->description.endpointId = readString(object, "endpoint");
data->description.endpointId = ReadJsonString(object, "endpoint");
const auto ssrcGroups = object.value("ssrc-groups").toArray();
data->description.videoSourceGroups.reserve(ssrcGroups.size());
@ -201,58 +202,11 @@ std::shared_ptr<VideoParams> ParseVideoParams(
}
data->description.videoSourceGroups.push_back({
.ssrcs = std::move(sources),
.semantics = readString(inner, "semantics"),
});
}
const auto payloadTypes = object.value("payload-types").toArray();
data->description.videoPayloadTypes.reserve(payloadTypes.size());
for (const auto &value : payloadTypes) {
const auto inner = value.toObject();
auto types = std::vector<GroupJoinPayloadVideoPayloadFeedbackType>();
{
const auto list = inner.value("rtcp-fbs").toArray();
types.reserve(list.size());
for (const auto &type : list) {
const auto inside = type.toObject();
types.push_back({
.type = readString(inside, "type"),
.subtype = readString(inside, "subtype"),
});
}
}
auto parameters = std::vector<std::pair<std::string, std::string>>();
{
const auto list = inner.value("parameters").toObject();
parameters.reserve(list.size());
for (auto i = list.begin(); i != list.end(); ++i) {
parameters.push_back({
i.key().toStdString(),
i.value().toString().toStdString(),
});
}
}
data->description.videoPayloadTypes.push_back({
.id = uint32_t(inner.value("id").toDouble()),
.name = readString(inner, "name"),
.clockrate = uint32_t(inner.value("clockrate").toDouble()),
.channels = uint32_t(inner.value("channels").toDouble()),
.feedbackTypes = std::move(types),
.parameters = std::move(parameters),
});
}
const auto extensionMap = object.value("rtp-hdrexts").toArray();
data->description.videoExtensionMap.reserve(extensionMap.size());
for (const auto &extension : extensionMap) {
const auto inner = extension.toObject();
data->description.videoExtensionMap.push_back({
uint32_t(inner.value("id").toDouble()),
readString(inner, "uri"),
.semantics = ReadJsonString(inner, "semantics"),
});
}
// videoPayloadTypes and videoExtensionMap will be in _commonVideoFields.
return data;
}
@ -950,7 +904,7 @@ void GroupCall::applyMeInCallLocally() {
| Flag::f_volume // Without flag the volume is reset to 100%.
| Flag::f_volume_by_admin // Self volume can only be set by admin.
| ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0))
| (videoMuted ? Flag::f_video_muted : Flag(0))
//| (videoMuted ? Flag(0) : Flag::f_video)
| (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0));
call->applyLocalUpdate(
MTP_updateGroupCallParticipants(
@ -966,7 +920,8 @@ void GroupCall::applyMeInCallLocally() {
MTP_int(volume),
MTPstring(), // Don't update about text in local updates.
MTP_long(raisedHandRating),
MTPDataJSON())),
MTPDataJSON(), // video
MTPDataJSON())), // presentation
MTP_int(0)).c_updateGroupCallParticipants());
}
@ -995,7 +950,7 @@ void GroupCall::applyParticipantLocally(
| (isMuted ? Flag::f_muted : Flag(0))
| (isMutedByYou ? Flag::f_muted_by_you : Flag(0))
| (participantPeer == _joinAs ? Flag::f_self : Flag(0))
| (participant->videoMuted ? Flag::f_video_muted : Flag(0))
//| (participant->videoMuted ? Flag(0) : Flag::f_video)
| (participant->raisedHandRating
? Flag::f_raise_hand_rating
: Flag(0));
@ -1013,7 +968,8 @@ void GroupCall::applyParticipantLocally(
MTP_int(volume.value_or(participant->volume)),
MTPstring(), // Don't update about text in local updates.
MTP_long(participant->raisedHandRating),
MTPDataJSON())),
MTPDataJSON(), // video
MTPDataJSON())), // presentation
MTP_int(0)).c_updateGroupCallParticipants());
}
@ -1195,13 +1151,18 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
} else if (_id != data.vid().v || !_instance) {
return;
}
const auto streamDcId = MTP::BareDcId(
data.vstream_dc_id().value_or_empty());
const auto params = data.vparams();
if (!params) {
if (const auto streamDcId = data.vstream_dc_id()) {
_broadcastDcId = MTP::BareDcId(streamDcId->v);
}
}
void GroupCall::handlePossibleCreateOrJoinResponse(
const MTPDupdateGroupCallConnection &data) {
if (data.is_presentation()) {
// #TODO calls
return;
}
params->match([&](const MTPDdataJSON &data) {
data.vparams().match([&](const MTPDdataJSON &data) {
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(
data.vdata().v,
@ -1222,60 +1183,117 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
});
if (document.object().value("stream").toBool()) {
if (!streamDcId) {
if (!_broadcastDcId) {
LOG(("Api Error: Empty stream_dc_id in groupCall."));
_broadcastDcId = _peer->session().mtp().mainDcId();
}
_broadcastDcId = streamDcId
? streamDcId
: _peer->session().mtp().mainDcId();
setInstanceMode(InstanceMode::Stream);
return;
}
const auto readString = [](
const QJsonObject &object,
const char *key) {
const QJsonObject &object,
const char *key) {
return object.value(key).toString().toStdString();
};
const auto root = document.object().value("transport").toObject();
const auto video = document.object().value("video").toObject();
auto payload = tgcalls::GroupJoinResponsePayload();
payload.ufrag = readString(root, "ufrag");
payload.pwd = readString(root, "pwd");
payload.serverVideoBandwidthProbingSsrc = uint32_t(
video.value("server_sources").toArray().at(0).toDouble());
payload.ufrag = ReadJsonString(root, "ufrag");
payload.pwd = ReadJsonString(root, "pwd");
const auto prints = root.value("fingerprints").toArray();
const auto candidates = root.value("candidates").toArray();
for (const auto &print : prints) {
const auto object = print.toObject();
payload.fingerprints.push_back({
.hash = readString(object, "hash"),
.setup = readString(object, "setup"),
.fingerprint = readString(object, "fingerprint"),
.hash = ReadJsonString(object, "hash"),
.setup = ReadJsonString(object, "setup"),
.fingerprint = ReadJsonString(object, "fingerprint"),
});
}
for (const auto &candidate : candidates) {
const auto object = candidate.toObject();
payload.candidates.push_back({
.port = readString(object, "port"),
.protocol = readString(object, "protocol"),
.network = readString(object, "network"),
.generation = readString(object, "generation"),
.id = readString(object, "id"),
.component = readString(object, "component"),
.foundation = readString(object, "foundation"),
.priority = readString(object, "priority"),
.ip = readString(object, "ip"),
.type = readString(object, "type"),
.tcpType = readString(object, "tcpType"),
.relAddr = readString(object, "relAddr"),
.relPort = readString(object, "relPort"),
.port = ReadJsonString(object, "port"),
.protocol = ReadJsonString(object, "protocol"),
.network = ReadJsonString(object, "network"),
.generation = ReadJsonString(object, "generation"),
.id = ReadJsonString(object, "id"),
.component = ReadJsonString(object, "component"),
.foundation = ReadJsonString(object, "foundation"),
.priority = ReadJsonString(object, "priority"),
.ip = ReadJsonString(object, "ip"),
.type = ReadJsonString(object, "type"),
.tcpType = ReadJsonString(object, "tcpType"),
.relAddr = ReadJsonString(object, "relAddr"),
.relPort = ReadJsonString(object, "relPort"),
});
}
parseCommonVideoFields(video);
setInstanceMode(InstanceMode::Rtc);
_instance->setJoinResponsePayload(payload, {});
addParticipantsToInstance();
});
}
void GroupCall::parseCommonVideoFields(const QJsonObject &root) {
using namespace tgcalls;
_commonVideoFields = std::make_unique<GroupParticipantDescription>();
const auto raw = _commonVideoFields.get();
const auto payloadTypes = root.value("payload-types").toArray();
raw->videoPayloadTypes.reserve(payloadTypes.size());
for (const auto &value : payloadTypes) {
const auto inner = value.toObject();
auto types = std::vector<GroupJoinPayloadVideoPayloadFeedbackType>();
{
const auto list = inner.value("rtcp-fbs").toArray();
types.reserve(list.size());
for (const auto &type : list) {
const auto inside = type.toObject();
types.push_back({
.type = ReadJsonString(inside, "type"),
.subtype = ReadJsonString(inside, "subtype"),
});
}
}
auto parameters = std::vector<std::pair<std::string, std::string>>();
{
const auto list = inner.value("parameters").toObject();
parameters.reserve(list.size());
for (auto i = list.begin(); i != list.end(); ++i) {
parameters.push_back({
i.key().toStdString(),
i.value().toString().toStdString(),
});
}
}
raw->videoPayloadTypes.push_back({
.id = uint32_t(inner.value("id").toDouble()),
.name = ReadJsonString(inner, "name"),
.clockrate = uint32_t(inner.value("clockrate").toDouble()),
.channels = uint32_t(inner.value("channels").toDouble()),
.feedbackTypes = std::move(types),
.parameters = std::move(parameters),
});
}
const auto extensionMap = root.value("rtp-hdrexts").toArray();
raw->videoExtensionMap.reserve(extensionMap.size());
for (const auto &extension : extensionMap) {
const auto inner = extension.toObject();
raw->videoExtensionMap.push_back({
uint32_t(inner.value("id").toDouble()),
ReadJsonString(inner, "uri"),
});
}
}
void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) {
if (data.vid().v == _id) {
LOG(("Call Info: Hangup after groupCallDiscarded."));
@ -1286,7 +1304,9 @@ void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) {
void GroupCall::addParticipantsToInstance() {
const auto real = lookupReal();
if (!real || (_instanceMode == InstanceMode::None)) {
if (!real
|| (_instanceMode == InstanceMode::None)
|| (_instanceMode == InstanceMode::Rtc && !_commonVideoFields)) {
return;
}
for (const auto &participant : real->participants()) {
@ -1297,10 +1317,15 @@ void GroupCall::addParticipantsToInstance() {
void GroupCall::prepareParticipantForAdding(
const Data::GroupCallParticipant &participant) {
_preparedParticipants.push_back(participant.videoParams
const auto withVideo = _commonVideoFields && participant.videoParams;
_preparedParticipants.push_back(withVideo
? participant.videoParams->description
: tgcalls::GroupParticipantDescription());
auto &added = _preparedParticipants.back();
if (withVideo) {
added.videoSourceGroups = _commonVideoFields->videoSourceGroups;
added.videoExtensionMap = _commonVideoFields->videoExtensionMap;
}
added.audioSsrc = participant.ssrc;
_unresolvedSsrcs.remove(added.audioSsrc);
for (const auto &group : added.videoSourceGroups) {
@ -1624,7 +1649,7 @@ void GroupCall::ensureControllerCreated() {
});
return result;
},
.videoContentType = tgcalls::VideoContentType::Screencast,
.videoContentType = tgcalls::VideoContentType::Generic,
};
if (Logs::DebugEnabled()) {
auto callLogFolder = cWorkingDir() + qsl("DebugLogs");
@ -1953,20 +1978,33 @@ void GroupCall::checkJoined() {
if (state() != State::Connecting || !_id || !_mySsrc) {
return;
}
auto sources = QVector<MTPint>(1, MTP_int(_mySsrc));
if (_screencastSsrc) {
sources.push_back(MTP_int(_screencastSsrc));
}
_api.request(MTPphone_CheckGroupCall(
inputCall(),
MTP_int(_mySsrc)
)).done([=](const MTPBool &result) {
if (!mtpIsTrue(result)) {
LOG(("Call Info: Rejoin after FALSE in checkGroupCall."));
MTP_vector<MTPint>(std::move(sources))
)).done([=](const MTPVector<MTPint> &result) {
if (!ranges::contains(result.v, MTP_int(_mySsrc))) {
LOG(("Call Info: Rejoin after no _mySsrc in checkGroupCall."));
rejoin();
} else if (state() == State::Connecting) {
_checkJoinedTimer.callOnce(kCheckJoinedTimeout);
}
if (_screencastSsrc
&& !ranges::contains(result.v, MTP_int(_screencastSsrc))) {
LOG(("Call Info: "
"Rejoin presentation after _screencastSsrc not found."));
// #TODO calls
}
}).fail([=](const MTP::Error &error) {
LOG(("Call Info: Rejoin after error '%1' in checkGroupCall."
LOG(("Call Info: Full rejoin after error '%1' in checkGroupCall."
).arg(error.type()));
rejoin();
if (_screencastSsrc) {
// #TODO calls
}
}).send();
}

View file

@ -143,6 +143,8 @@ public:
void join(const MTPInputGroupCall &inputCall);
void handleUpdate(const MTPUpdate &update);
void handlePossibleCreateOrJoinResponse(const MTPDupdateGroupCall &data);
void handlePossibleCreateOrJoinResponse(
const MTPDupdateGroupCallConnection &data);
void changeTitle(const QString &title);
void toggleRecording(bool enabled, const QString &title);
[[nodiscard]] bool recordingStoppedByMe() const {
@ -299,6 +301,7 @@ private:
void handlePossibleDiscarded(const MTPDgroupCallDiscarded &data);
void handleUpdate(const MTPDupdateGroupCall &data);
void handleUpdate(const MTPDupdateGroupCallParticipants &data);
void parseCommonVideoFields(const QJsonObject &root);
void ensureControllerCreated();
void destroyController();
@ -391,6 +394,8 @@ private:
uint64 _id = 0;
uint64 _accessHash = 0;
uint32 _mySsrc = 0;
uint32 _screencastSsrc = 0;
std::unique_ptr<tgcalls::GroupParticipantDescription> _commonVideoFields;
TimeId _scheduleDate = 0;
base::flat_set<uint32> _mySsrcs;
mtpRequestId _createRequestId = 0;

View file

@ -549,8 +549,8 @@ void GroupCall::applyParticipantsSlice(
.peer = participantPeer,
.videoParams = (hasVideoParamsInformation
? Calls::ParseVideoParams(
(data.vparams()
? data.vparams()->c_dataJSON().vdata().v
(data.vvideo()
? data.vvideo()->c_dataJSON().vdata().v
: QByteArray()),
(i != end(_participants)
? i->videoParams
@ -568,7 +568,7 @@ void GroupCall::applyParticipantsSlice(
.muted = data.is_muted(),
.mutedByMe = mutedByMe,
.canSelfUnmute = canSelfUnmute,
.videoMuted = data.is_video_muted(),
.videoMuted = (data.vvideo() == nullptr),
.onlyMinLoaded = onlyMinLoaded,
};
if (i == end(_participants)) {