Apply group call updates.

This commit is contained in:
John Preston 2020-11-24 14:56:46 +03:00
parent 33941ad1b9
commit 25f3c14780
9 changed files with 357 additions and 61 deletions

View file

@ -191,6 +191,7 @@ private:
Ended,
Failed,
};
void handleRequestError(const RPCError &error);
void handleControllerError(const QString &error);
void finish(

View file

@ -25,7 +25,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
//#include "calls/calls_panel.h"
//#include "webrtc/webrtc_video_track.h"
//#include "webrtc/webrtc_media_devices.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_group_call.h"
//#include "data/data_session.h"
//#include "facades.h"
@ -49,6 +51,7 @@ GroupCall::GroupCall(
, _channel(channel)
, _api(&_channel->session().mtp()) {
if (inputCall.c_inputGroupCall().vid().v) {
_state = State::Joining;
join(inputCall);
} else {
start();
@ -59,19 +62,51 @@ GroupCall::~GroupCall() {
destroyController();
}
void GroupCall::setState(State state) {
if (_state.current() == State::Failed) {
return;
} else if (_state.current() == State::FailedHangingUp
&& state != State::Failed) {
return;
}
if (_state.current() == state) {
return;
}
_state = state;
if (false
|| state == State::Ended
|| state == State::Failed) {
// Destroy controller before destroying Call Panel,
// so that the panel hide animation is smooth.
destroyController();
}
switch (state) {
case State::Ended:
_delegate->groupCallFinished(this);
break;
case State::Failed:
_delegate->groupCallFailed(this);
break;
}
}
void GroupCall::start() {
const auto randomId = rand_value<int32>();
_api.request(MTPphone_CreateGroupCall(
_channel->inputChannel,
MTP_int(randomId)
)).done([=](const MTPUpdates &result) {
_acceptFields = true;
_channel->session().api().applyUpdates(result);
_acceptFields = false;
}).fail([=](const RPCError &error) {
int a = error.code();
}).send();
}
void GroupCall::join(const MTPInputGroupCall &inputCall) {
setState(State::Joining);
inputCall.match([&](const MTPDinputGroupCall &data) {
_id = data.vid().v;
_accessHash = data.vaccess_hash().v;
@ -91,10 +126,11 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
}
auto root = QJsonObject();
const auto ssrc = payload.ssrc;
root.insert("ufrag", QString::fromStdString(payload.ufrag));
root.insert("pwd", QString::fromStdString(payload.pwd));
root.insert("fingerprints", fingerprints);
root.insert("ssrc", int(payload.ssrc));
root.insert("ssrc", double(payload.ssrc));
const auto json = QJsonDocument(root).toJson(
QJsonDocument::Compact);
@ -105,6 +141,9 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
inputCall,
MTP_dataJSON(MTP_bytes(json))
)).done([=](const MTPUpdates &updates) {
_mySsrc = ssrc;
setState(State::Joined);
_channel->session().api().applyUpdates(updates);
}).fail([=](const RPCError &error) {
int a = error.code();
@ -112,6 +151,77 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
});
});
});
_channel->setCall(inputCall);
_channel->session().changes().peerFlagsValue(
_channel,
Data::PeerUpdate::Flag::GroupCall
) | rpl::start_with_next([=] {
checkParticipants();
}, _lifetime);
}
void GroupCall::checkParticipants() {
if (!joined()) {
return;
}
const auto call = _channel->call();
if (!call || call->id() != _id) {
finish(FinishType::Ended);
return;
}
const auto &sources = call->sources();
if (sources.size() != call->fullCount() || sources.empty()) {
call->reload();
return;
}
auto ssrcs = std::vector<uint32_t>();
ssrcs.reserve(sources.size());
for (const auto source : sources) {
if (source != _mySsrc) {
ssrcs.push_back(source);
}
}
_instance->setSsrcs(std::move(ssrcs));
_instance->setIsMuted(false);
}
void GroupCall::hangup() {
finish(FinishType::Ended);
}
void GroupCall::finish(FinishType type) {
Expects(type != FinishType::None);
const auto finalState = (type == FinishType::Ended)
? State::Ended
: State::Failed;
const auto hangupState = (type == FinishType::Ended)
? State::HangingUp
: State::FailedHangingUp;
const auto state = _state.current();
if (state == State::HangingUp
|| state == State::FailedHangingUp
|| state == State::Ended
|| state == State::Failed) {
return;
}
if (!joined()) {
setState(finalState);
return;
}
setState(hangupState);
_api.request(MTPphone_LeaveGroupCall(
inputCall()
)).done([=](const MTPUpdates &result) {
// Here 'this' could be destroyed by updates, so we set Ended after
// updates being handled, but in a guarded way.
crl::on_main(this, [=] { setState(finalState); });
_channel->session().api().applyUpdates(result);
}).fail([=](const RPCError &error) {
setState(finalState);
}).send();
}
void GroupCall::setMuted(bool mute) {
@ -123,7 +233,13 @@ void GroupCall::setMuted(bool mute) {
bool GroupCall::handleUpdate(const MTPGroupCall &call) {
return call.match([&](const MTPDgroupCall &data) {
if (_id != data.vid().v
if (_acceptFields) {
if (_instance || _id) {
return false;
}
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
return true;
} else if (_id != data.vid().v
|| _accessHash != data.vaccess_hash().v
|| !_instance) {
return false;
@ -182,35 +298,10 @@ bool GroupCall::handleUpdate(const MTPGroupCall &call) {
});
}
_instance->setJoinResponsePayload(payload);
_api.request(MTPphone_GetGroupParticipants(
inputCall(),
MTP_int(0),
MTP_int(10)
)).done([=](const MTPphone_GroupParticipants &result) {
auto sources = std::vector<uint32_t>();
result.match([&](const MTPDphone_groupParticipants &data) {
for (const auto &p : data.vparticipants().v) {
p.match([&](const MTPDgroupCallParticipant &data) {
if (data.vuser_id().v != _channel->session().userId()) {
sources.push_back(data.vsource().v);
}
});
}
});
_instance->setSsrcs(std::move(sources));
_instance->setIsMuted(false);
}).fail([=](const RPCError &error) {
int a = error.code();
}).send();
checkParticipants();
});
}
return true;
}, [&](const MTPDgroupCallPrivate &data) {
if (_instance || _id) {
return false;
}
join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
return true;
}, [&](const MTPDgroupCallDiscarded &data) {
if (data.vid().v != _id) {
return false;

View file

@ -25,6 +25,9 @@ public:
public:
virtual ~Delegate() = default;
virtual void groupCallFinished(not_null<GroupCall*> call) = 0;
virtual void groupCallFailed(not_null<GroupCall*> call) = 0;
};
GroupCall(
@ -38,6 +41,7 @@ public:
}
void start();
void hangup();
void join(const MTPInputGroupCall &inputCall);
bool handleUpdate(const MTPGroupCall &call);
@ -48,6 +52,25 @@ public:
[[nodiscard]] rpl::producer<bool> mutedValue() const {
return _muted.value();
}
[[nodiscard]] bool joined() const {
return _mySsrc != 0;
}
enum State {
Creating,
Joining,
Joined,
FailedHangingUp,
Failed,
HangingUp,
Ended,
};
[[nodiscard]] State state() const {
return _state.current();
}
[[nodiscard]] rpl::producer<State> stateValue() const {
return _state.value();
}
void setCurrentAudioDevice(bool input, const QString &deviceId);
void setAudioVolume(bool input, float level);
@ -58,22 +81,34 @@ public:
}
private:
enum class FinishType {
None,
Ended,
Failed,
};
void handleRequestError(const RPCError &error);
void handleControllerError(const QString &error);
void createAndStartController();
void destroyController();
void checkParticipants();
void setState(State state);
void finish(FinishType type);
[[nodiscard]] MTPInputGroupCall inputCall() const;
const not_null<Delegate*> _delegate;
const not_null<ChannelData*> _channel;
MTP::Sender _api;
crl::time _startTime = 0;
rpl::variable<State> _state = State::Creating;
rpl::variable<bool> _muted = false;
bool _acceptFields = false;
uint64 _id = 0;
uint64 _accessHash = 0;
uint32 _mySsrc = 0;
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;

View file

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_call.h"
#include "calls/calls_panel.h"
#include "data/data_user.h"
#include "data/data_group_call.h"
#include "data/data_channel.h"
#include "data/data_session.h"
#include "media/audio/media_audio_track.h"
@ -93,6 +94,18 @@ void Instance::callRedial(not_null<Call*> call) {
}
}
void Instance::groupCallFinished(not_null<GroupCall*> call) {
crl::on_main(call, [=] {
destroyGroupCall(call);
});
}
void Instance::groupCallFailed(not_null<GroupCall*> call) {
crl::on_main(call, [=] {
destroyGroupCall(call);
});
}
void Instance::playSound(Sound sound) {
switch (sound) {
case Sound::Busy: {
@ -351,6 +364,12 @@ void Instance::handleCallUpdate(
void Instance::handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPGroupCall &call) {
const auto callId = call.match([](const auto &data) {
return data.vid().v;
});
if (const auto existing = session->data().groupCall(callId)) {
existing->applyUpdate(call);
}
if (_currentGroupCall) {
_currentGroupCall->handleUpdate(call);
}
@ -359,6 +378,12 @@ void Instance::handleGroupCallUpdate(
void Instance::handleGroupCallUpdate(
not_null<Main::Session*> session,
const MTPDupdateGroupCallParticipants &update) {
const auto callId = update.vcall().match([](const auto &data) {
return data.vid().v;
});
if (const auto existing = session->data().groupCall(callId)) {
existing->applyUpdate(update);
}
}
void Instance::handleSignalingData(
@ -424,7 +449,7 @@ void Instance::requestPermissionOrFail(Platform::PermissionType type, Fn<void()>
_currentCall->hangup();
}
if (inGroupCall()) {
//_currentGroupCall->hangup(); // #TODO calls
_currentGroupCall->hangup();
}
Ui::show(Box<ConfirmBox>(tr::lng_no_mic_permission(tr::now), tr::lng_menu_settings(tr::now), crl::guard(this, [=] {
Platform::OpenSystemSettingsForPermission(type);

View file

@ -70,6 +70,9 @@ private:
requestPermissionsOrFail(std::move(onSuccess));
}
void groupCallFinished(not_null<GroupCall*> call) override;
void groupCallFailed(not_null<GroupCall*> call) override;
using Sound = Call::Delegate::Sound;
void playSound(Sound sound) override;
void createCall(not_null<UserData*> user, Call::Type type, bool video);

View file

@ -29,10 +29,19 @@ GroupCall::GroupCall(
, _accessHash(accessHash) {
}
GroupCall::~GroupCall() {
_channel->session().api().request(_participantsRequestId).cancel();
_channel->session().api().request(_reloadRequestId).cancel();
}
uint64 GroupCall::id() const {
return _id;
}
not_null<ChannelData*> GroupCall::channel() const {
return _channel;
}
MTPInputGroupCall GroupCall::input() const {
return MTP_inputGroupCall(MTP_long(_id), MTP_long(_accessHash));
}
@ -42,8 +51,12 @@ auto GroupCall::participants() const
return _participants;
}
const base::flat_set<uint32> &GroupCall::sources() const {
return _sources;
}
void GroupCall::requestParticipants() {
if (_participantsRequestId) {
if (_participantsRequestId || _reloadRequestId) {
return;
} else if (_participants.size() >= _fullCount && _allReceived) {
return;
@ -58,31 +71,9 @@ void GroupCall::requestParticipants() {
MTP_int(kRequestPerPage)
)).done([=](const MTPphone_GroupParticipants &result) {
result.match([&](const MTPDphone_groupParticipants &data) {
_fullCount = data.vcount().v;
_channel->owner().processUsers(data.vusers());
for (const auto &p : data.vparticipants().v) {
p.match([&](const MTPDgroupCallParticipant &data) {
const auto userId = data.vuser_id().v;
const auto user = _channel->owner().user(userId);
const auto value = Participant{
.user = user,
.date = data.vdate().v,
.source = data.vsource().v,
.muted = data.is_muted(),
.canSelfUnmute = data.is_can_self_unmute(),
.left = data.is_left()
};
const auto i = ranges::find(
_participants,
user,
&Participant::user);
if (i == end(_participants)) {
_participants.push_back(value);
} else {
*i = value;
}
});
}
applyParticipantsSlice(data.vparticipants().v);
_fullCount = data.vcount().v;
if (!_allReceived
&& (data.vparticipants().v.size() < kRequestPerPage)) {
_allReceived = true;
@ -91,16 +82,17 @@ void GroupCall::requestParticipants() {
_fullCount = _participants.size();
}
});
ranges::sort(_participants, std::greater<>(), &Participant::date);
_channel->session().changes().peerUpdated(
_channel,
PeerUpdate::Flag::GroupCall);
_participantsRequestId = 0;
}).fail([=](const RPCError &error) {
_allReceived = true;
_fullCount = _participants.size();
_allReceived = true;
_channel->session().changes().peerUpdated(
_channel,
PeerUpdate::Flag::GroupCall);
_participantsRequestId = 0;
}).send();
}
@ -112,4 +104,118 @@ bool GroupCall::participantsLoaded() const {
return _allReceived;
}
void GroupCall::applyUpdate(const MTPGroupCall &update) {
applyCall(update, false);
}
void GroupCall::applyCall(const MTPGroupCall &call, bool force) {
call.match([&](const MTPDgroupCall &data) {
const auto changed = (_version != data.vversion().v)
|| (_fullCount != data.vparticipants_count().v);
if (!force && !changed) {
return;
} else if (!force && _version > data.vversion().v) {
reload();
return;
}
_version = data.vversion().v;
_fullCount = data.vparticipants_count().v;
}, [&](const MTPDgroupCallDiscarded &data) {
const auto changed = (_duration != data.vduration().v)
|| !_finished;
if (!force && !changed) {
return;
}
_finished = true;
_duration = data.vduration().v;
});
_channel->session().changes().peerUpdated(
_channel,
PeerUpdate::Flag::GroupCall);
}
void GroupCall::reload() {
if (_reloadRequestId) {
return;
} else if (_participantsRequestId) {
_channel->session().api().request(_participantsRequestId).cancel();
_participantsRequestId = 0;
}
_reloadRequestId = _channel->session().api().request(
MTPphone_GetGroupCall(input())
).done([=](const MTPphone_GroupCall &result) {
result.match([&](const MTPDphone_groupCall &data) {
_channel->owner().processUsers(data.vusers());
_participants.clear();
_sources.clear();
applyParticipantsSlice(data.vparticipants().v);
for (const auto &source : data.vsources().v) {
_sources.emplace(source.v);
}
_fullCount = _sources.size();
if (_participants.size() > _fullCount) {
_fullCount = _participants.size();
}
_allReceived = (_fullCount == _participants.size());
applyCall(data.vcall(), true);
});
_reloadRequestId = 0;
}).fail([=](const RPCError &error) {
_reloadRequestId = 0;
}).send();
}
void GroupCall::applyParticipantsSlice(
const QVector<MTPGroupCallParticipant> &list) {
for (const auto &participant : list) {
participant.match([&](const MTPDgroupCallParticipant &data) {
const auto userId = data.vuser_id().v;
const auto user = _channel->owner().user(userId);
const auto i = ranges::find(
_participants,
user,
&Participant::user);
if (data.is_left()) {
if (i != end(_participants)) {
_sources.remove(i->source);
_participants.erase(i);
}
if (_fullCount > _participants.size()) {
--_fullCount;
}
return;
}
const auto value = Participant{
.user = user,
.date = data.vdate().v,
.source = uint32(data.vsource().v),
.muted = data.is_muted(),
.canSelfUnmute = data.is_can_self_unmute(),
};
if (i == end(_participants)) {
_participants.push_back(value);
++_fullCount;
} else {
*i = value;
}
_sources.emplace(uint32(data.vsource().v));
});
}
ranges::sort(_participants, std::greater<>(), &Participant::date);
}
void GroupCall::applyUpdate(const MTPDupdateGroupCallParticipants &update) {
if (update.vversion().v <= _version) {
return;
} else if (update.vversion().v != _version + 1) {
reload();
return;
}
_version = update.vversion().v;
applyParticipantsSlice(update.vparticipants().v);
_channel->session().changes().peerUpdated(
_channel,
PeerUpdate::Flag::GroupCall);
}
} // namespace Data

View file

@ -15,38 +15,52 @@ namespace Data {
class GroupCall final {
public:
GroupCall(not_null<ChannelData*> channel, uint64 id, uint64 accessHash);
~GroupCall();
[[nodiscard]] uint64 id() const;
[[nodiscard]] not_null<ChannelData*> channel() const;
[[nodiscard]] MTPInputGroupCall input() const;
struct Participant {
not_null<UserData*> user;
TimeId date = 0;
int source = 0;
uint32 source = 0;
bool muted = false;
bool canSelfUnmute = false;
bool left = false;
};
[[nodiscard]] auto participants() const
-> const std::vector<Participant> &;
[[nodiscard]] const base::flat_set<uint32> &sources() const;
void requestParticipants();
[[nodiscard]] bool participantsLoaded() const;
void applyUpdate(const MTPGroupCall &update);
void applyUpdate(const MTPDupdateGroupCallParticipants &update);
[[nodiscard]] int fullCount() const;
void reload();
[[nodiscard]] bool finished() const;
[[nodiscard]] int duration() const;
private:
void applyCall(const MTPGroupCall &call, bool force);
void applyParticipantsSlice(const QVector<MTPGroupCallParticipant> &list);
const not_null<ChannelData*> _channel;
const uint64 _id = 0;
const uint64 _accessHash = 0;
int _version = 0;
UserId _adminId = 0;
uint64 _reflectorId = 0;
mtpRequestId _participantsRequestId = 0;
mtpRequestId _reloadRequestId = 0;
std::vector<Participant> _participants;
base::flat_set<uint32> _sources;
int _fullCount = 0;
int _duration = 0;
bool _finished = false;
bool _allReceived = false;
};

View file

@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
#include "data/stickers/data_stickers.h"
#include "data/data_changes.h"
#include "data/data_group_call.h"
#include "data/data_media_types.h"
#include "data/data_folder.h"
#include "data/data_channel.h"
@ -791,6 +792,19 @@ void Session::applyMaximumChatVersions(const MTPVector<MTPChat> &data) {
}
}
void Session::registerGroupCall(not_null<GroupCall*> call) {
_groupCalls.emplace(call->id(), call);
}
void Session::unregisterGroupCall(not_null<GroupCall*> call) {
_groupCalls.remove(call->id());
}
GroupCall *Session::groupCall(uint64 callId) const {
const auto i = _groupCalls.find(callId);
return (i != end(_groupCalls)) ? i->second.get() : nullptr;
}
PeerData *Session::peerByUsername(const QString &username) const {
const auto uname = username.trimmed();
for (const auto &[peerId, peer] : _peers) {

View file

@ -60,6 +60,7 @@ class Histories;
class DocumentMedia;
class PhotoMedia;
class Stickers;
class GroupCall;
class Session final {
public:
@ -153,6 +154,10 @@ public:
void applyMaximumChatVersions(const MTPVector<MTPChat> &data);
void registerGroupCall(not_null<GroupCall*> call);
void unregisterGroupCall(not_null<GroupCall*> call);
GroupCall *groupCall(uint64 callId) const;
void enumerateUsers(Fn<void(not_null<UserData*>)> action) const;
void enumerateGroups(Fn<void(not_null<PeerData*>)> action) const;
void enumerateChannels(Fn<void(not_null<ChannelData*>)> action) const;
@ -906,6 +911,8 @@ private:
base::flat_set<not_null<ViewElement*>> _heavyViewParts;
base::flat_map<uint64, not_null<GroupCall*>> _groupCalls;
History *_topPromoted = nullptr;
NotifySettings _defaultUserNotifySettings;