"Fossies" - the Fresh Open Source Software Archive

Member "tdesktop-2.6.1/Telegram/SourceFiles/calls/calls_group_call.cpp" (24 Feb 2021, 33520 Bytes) of package /linux/misc/tdesktop-2.6.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "calls_group_call.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.5.9_vs_2.6.0.

    1 /*
    2 This file is part of Telegram Desktop,
    3 the official desktop application for the Telegram messaging service.
    4 
    5 For license and copyright information please follow this link:
    6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
    7 */
    8 #include "calls/calls_group_call.h"
    9 
   10 #include "calls/calls_group_common.h"
   11 #include "main/main_session.h"
   12 #include "api/api_send_progress.h"
   13 #include "apiwrap.h"
   14 #include "lang/lang_keys.h"
   15 #include "lang/lang_hardcoded.h"
   16 #include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
   17 #include "ui/toasts/common_toasts.h"
   18 #include "base/unixtime.h"
   19 #include "core/application.h"
   20 #include "core/core_settings.h"
   21 #include "data/data_changes.h"
   22 #include "data/data_user.h"
   23 #include "data/data_chat.h"
   24 #include "data/data_channel.h"
   25 #include "data/data_group_call.h"
   26 #include "data/data_session.h"
   27 #include "base/global_shortcuts.h"
   28 #include "base/openssl_help.h"
   29 #include "webrtc/webrtc_media_devices.h"
   30 #include "webrtc/webrtc_create_adm.h"
   31 
   32 #include <tgcalls/group/GroupInstanceImpl.h>
   33 
   34 #include <QtCore/QJsonDocument>
   35 #include <QtCore/QJsonObject>
   36 #include <QtCore/QJsonArray>
   37 
   38 namespace tgcalls {
   39 class GroupInstanceImpl;
   40 } // namespace tgcalls
   41 
   42 namespace Calls {
   43 namespace {
   44 
   45 constexpr auto kMaxInvitePerSlice = 10;
   46 constexpr auto kCheckLastSpokeInterval = crl::time(1000);
   47 constexpr auto kCheckJoinedTimeout = 4 * crl::time(1000);
   48 constexpr auto kUpdateSendActionEach = crl::time(500);
   49 constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000);
   50 
   51 [[nodiscard]] std::unique_ptr<Webrtc::MediaDevices> CreateMediaDevices() {
   52     const auto &settings = Core::App().settings();
   53     return Webrtc::CreateMediaDevices(
   54         settings.callInputDeviceId(),
   55         settings.callOutputDeviceId(),
   56         settings.callVideoInputDeviceId());
   57 }
   58 
   59 [[nodiscard]] const Data::GroupCall::Participant *LookupParticipant(
   60         not_null<PeerData*> chat,
   61         uint64 id,
   62         not_null<UserData*> user) {
   63     const auto call = chat->groupCall();
   64     if (!id || !call || call->id() != id) {
   65         return nullptr;
   66     }
   67     const auto &participants = call->participants();
   68     const auto i = ranges::find(
   69         participants,
   70         user,
   71         &Data::GroupCall::Participant::user);
   72     return (i != end(participants)) ? &*i : nullptr;
   73 }
   74 
   75 } // namespace
   76 
   77 [[nodiscard]] bool IsGroupCallAdmin(
   78         not_null<PeerData*> peer,
   79         not_null<UserData*> user) {
   80     if (const auto chat = peer->asChat()) {
   81         return chat->admins.contains(user)
   82             || (chat->creator == user->bareId());
   83     } else if (const auto group = peer->asMegagroup()) {
   84         if (const auto mgInfo = group->mgInfo.get()) {
   85             if (mgInfo->creator == user) {
   86                 return true;
   87             }
   88             const auto i = mgInfo->lastAdmins.find(user);
   89             if (i == mgInfo->lastAdmins.end()) {
   90                 return false;
   91             }
   92             const auto &rights = i->second.rights;
   93             return rights.c_chatAdminRights().is_manage_call();
   94         }
   95     }
   96     return false;
   97 }
   98 
   99 GroupCall::GroupCall(
  100     not_null<Delegate*> delegate,
  101     not_null<PeerData*> peer,
  102     const MTPInputGroupCall &inputCall)
  103 : _delegate(delegate)
  104 , _peer(peer)
  105 , _history(peer->owner().history(peer))
  106 , _api(&peer->session().mtp())
  107 , _lastSpokeCheckTimer([=] { checkLastSpoke(); })
  108 , _checkJoinedTimer([=] { checkJoined(); })
  109 , _pushToTalkCancelTimer([=] { pushToTalkCancel(); })
  110 , _connectingSoundTimer([=] { playConnectingSoundOnce(); })
  111 , _mediaDevices(CreateMediaDevices()) {
  112     _muted.value(
  113     ) | rpl::combine_previous(
  114     ) | rpl::start_with_next([=](MuteState previous, MuteState state) {
  115         if (_instance) {
  116             updateInstanceMuteState();
  117         }
  118         if (_mySsrc) {
  119             maybeSendMutedUpdate(previous);
  120         }
  121     }, _lifetime);
  122 
  123     checkGlobalShortcutAvailability();
  124 
  125     const auto id = inputCall.c_inputGroupCall().vid().v;
  126     if (id) {
  127         if (const auto call = _peer->groupCall(); call && call->id() == id) {
  128             if (!_peer->canManageGroupCall() && call->joinMuted()) {
  129                 _muted = MuteState::ForceMuted;
  130             }
  131         }
  132         _state = State::Joining;
  133         join(inputCall);
  134     } else {
  135         start();
  136     }
  137 
  138     _mediaDevices->audioInputId(
  139     ) | rpl::start_with_next([=](QString id) {
  140         _audioInputId = id;
  141         if (_instance) {
  142             _instance->setAudioInputDevice(id.toStdString());
  143         }
  144     }, _lifetime);
  145 
  146     _mediaDevices->audioOutputId(
  147     ) | rpl::start_with_next([=](QString id) {
  148         _audioOutputId = id;
  149         if (_instance) {
  150             _instance->setAudioOutputDevice(id.toStdString());
  151         }
  152     }, _lifetime);
  153 }
  154 
  155 GroupCall::~GroupCall() {
  156     destroyController();
  157 }
  158 
  159 void GroupCall::checkGlobalShortcutAvailability() {
  160     auto &settings = Core::App().settings();
  161     if (!settings.groupCallPushToTalk()) {
  162         return;
  163     } else if (!base::GlobalShortcutsAllowed()) {
  164         settings.setGroupCallPushToTalk(false);
  165         Core::App().saveSettingsDelayed();
  166     }
  167 }
  168 
  169 void GroupCall::setState(State state) {
  170     if (_state.current() == State::Failed) {
  171         return;
  172     } else if (_state.current() == State::FailedHangingUp
  173         && state != State::Failed) {
  174         return;
  175     }
  176     if (_state.current() == state) {
  177         return;
  178     }
  179     _state = state;
  180 
  181     if (state == State::Joined) {
  182         stopConnectingSound();
  183         if (!_hadJoinedState) {
  184             _hadJoinedState = true;
  185             applyGlobalShortcutChanges();
  186             _delegate->groupCallPlaySound(Delegate::GroupCallSound::Started);
  187         }
  188         if (const auto call = _peer->groupCall(); call && call->id() == _id) {
  189             call->setInCall();
  190         }
  191     } else if (state == State::Connecting || state == State::Joining) {
  192         if (_hadJoinedState) {
  193             playConnectingSound();
  194         }
  195     } else {
  196         stopConnectingSound();
  197     }
  198 
  199     if (false
  200         || state == State::Ended
  201         || state == State::Failed) {
  202         // Destroy controller before destroying Call Panel,
  203         // so that the panel hide animation is smooth.
  204         destroyController();
  205     }
  206     switch (state) {
  207     case State::HangingUp:
  208     case State::FailedHangingUp:
  209         _delegate->groupCallPlaySound(Delegate::GroupCallSound::Ended);
  210         break;
  211     case State::Ended:
  212         _delegate->groupCallFinished(this);
  213         break;
  214     case State::Failed:
  215         _delegate->groupCallFailed(this);
  216         break;
  217     case State::Connecting:
  218         if (!_checkJoinedTimer.isActive()) {
  219             _checkJoinedTimer.callOnce(kCheckJoinedTimeout);
  220         }
  221         break;
  222     }
  223 }
  224 
  225 void GroupCall::playConnectingSound() {
  226     if (_connectingSoundTimer.isActive()) {
  227         return;
  228     }
  229     playConnectingSoundOnce();
  230     _connectingSoundTimer.callEach(kPlayConnectingEach);
  231 }
  232 
  233 void GroupCall::stopConnectingSound() {
  234     _connectingSoundTimer.cancel();
  235 }
  236 
  237 void GroupCall::playConnectingSoundOnce() {
  238     _delegate->groupCallPlaySound(Delegate::GroupCallSound::Connecting);
  239 }
  240 
  241 void GroupCall::start() {
  242     _createRequestId = _api.request(MTPphone_CreateGroupCall(
  243         _peer->input,
  244         MTP_int(openssl::RandomValue<int32>())
  245     )).done([=](const MTPUpdates &result) {
  246         _acceptFields = true;
  247         _peer->session().api().applyUpdates(result);
  248         _acceptFields = false;
  249     }).fail([=](const RPCError &error) {
  250         LOG(("Call Error: Could not create, error: %1"
  251             ).arg(error.type()));
  252         hangup();
  253         if (error.type() == u"GROUPCALL_ANONYMOUS_FORBIDDEN"_q) {
  254             Ui::ShowMultilineToast({
  255                 .text = { tr::lng_group_call_no_anonymous(tr::now) },
  256             });
  257         }
  258     }).send();
  259 }
  260 
  261 void GroupCall::join(const MTPInputGroupCall &inputCall) {
  262     setState(State::Joining);
  263     if (const auto chat = _peer->asChat()) {
  264         chat->setGroupCall(inputCall);
  265     } else if (const auto group = _peer->asMegagroup()) {
  266         group->setGroupCall(inputCall);
  267     } else {
  268         Unexpected("Peer type in GroupCall::join.");
  269     }
  270 
  271     inputCall.match([&](const MTPDinputGroupCall &data) {
  272         _id = data.vid().v;
  273         _accessHash = data.vaccess_hash().v;
  274         rejoin();
  275     });
  276 
  277     using Update = Data::GroupCall::ParticipantUpdate;
  278     _peer->groupCall()->participantUpdated(
  279     ) | rpl::filter([=](const Update &update) {
  280         return (_instance != nullptr);
  281     }) | rpl::start_with_next([=](const Update &update) {
  282         if (!update.now) {
  283             _instance->removeSsrcs({ update.was->ssrc });
  284         } else {
  285             const auto &now = *update.now;
  286             const auto &was = update.was;
  287             const auto volumeChanged = was
  288                 ? (was->volume != now.volume || was->mutedByMe != now.mutedByMe)
  289                 : (now.volume != Group::kDefaultVolume || now.mutedByMe);
  290             if (volumeChanged) {
  291                 _instance->setVolume(
  292                     now.ssrc,
  293                     (now.mutedByMe
  294                         ? 0.
  295                         : (now.volume
  296                             / float64(Group::kDefaultVolume))));
  297             }
  298         }
  299     }, _lifetime);
  300 
  301     SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) {
  302         _peer = group;
  303     });
  304 }
  305 
  306 void GroupCall::rejoin() {
  307     if (state() != State::Joining
  308         && state() != State::Joined
  309         && state() != State::Connecting) {
  310         return;
  311     }
  312 
  313     _mySsrc = 0;
  314     setState(State::Joining);
  315     createAndStartController();
  316     applySelfInCallLocally();
  317     LOG(("Call Info: Requesting join payload."));
  318 
  319     const auto weak = base::make_weak(this);
  320     _instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) {
  321         crl::on_main(weak, [=, payload = std::move(payload)]{
  322             auto fingerprints = QJsonArray();
  323             for (const auto print : payload.fingerprints) {
  324                 auto object = QJsonObject();
  325                 object.insert("hash", QString::fromStdString(print.hash));
  326                 object.insert("setup", QString::fromStdString(print.setup));
  327                 object.insert(
  328                     "fingerprint",
  329                     QString::fromStdString(print.fingerprint));
  330                 fingerprints.push_back(object);
  331             }
  332 
  333             auto root = QJsonObject();
  334             const auto ssrc = payload.ssrc;
  335             root.insert("ufrag", QString::fromStdString(payload.ufrag));
  336             root.insert("pwd", QString::fromStdString(payload.pwd));
  337             root.insert("fingerprints", fingerprints);
  338             root.insert("ssrc", double(payload.ssrc));
  339 
  340             LOG(("Call Info: Join payload received, joining with ssrc: %1."
  341                 ).arg(ssrc));
  342 
  343             const auto json = QJsonDocument(root).toJson(
  344                 QJsonDocument::Compact);
  345             const auto wasMuteState = muted();
  346             _api.request(MTPphone_JoinGroupCall(
  347                 MTP_flags((wasMuteState != MuteState::Active)
  348                     ? MTPphone_JoinGroupCall::Flag::f_muted
  349                     : MTPphone_JoinGroupCall::Flag(0)),
  350                 inputCall(),
  351                 MTP_dataJSON(MTP_bytes(json))
  352             )).done([=](const MTPUpdates &updates) {
  353                 _mySsrc = ssrc;
  354                 setState(_instanceConnected
  355                     ? State::Joined
  356                     : State::Connecting);
  357                 applySelfInCallLocally();
  358                 maybeSendMutedUpdate(wasMuteState);
  359                 _peer->session().api().applyUpdates(updates);
  360             }).fail([=](const RPCError &error) {
  361                 const auto type = error.type();
  362                 LOG(("Call Error: Could not join, error: %1").arg(type));
  363 
  364                 if (type == u"GROUPCALL_SSRC_DUPLICATE_MUCH") {
  365                     rejoin();
  366                     return;
  367                 }
  368 
  369                 hangup();
  370                 Ui::ShowMultilineToast({
  371                     .text = { type == u"GROUPCALL_ANONYMOUS_FORBIDDEN"_q
  372                         ? tr::lng_group_call_no_anonymous(tr::now)
  373                         : type == u"GROUPCALL_PARTICIPANTS_TOO_MUCH"_q
  374                         ? tr::lng_group_call_too_many(tr::now)
  375                         : type == u"GROUPCALL_FORBIDDEN"_q
  376                         ? tr::lng_group_not_accessible(tr::now)
  377                         : Lang::Hard::ServerError() },
  378                 });
  379             }).send();
  380         });
  381     });
  382 }
  383 
  384 void GroupCall::applySelfInCallLocally() {
  385     const auto call = _peer->groupCall();
  386     if (!call || call->id() != _id) {
  387         return;
  388     }
  389     using Flag = MTPDgroupCallParticipant::Flag;
  390     const auto &participants = call->participants();
  391     const auto self = _peer->session().user();
  392     const auto i = ranges::find(
  393         participants,
  394         self,
  395         &Data::GroupCall::Participant::user);
  396     const auto date = (i != end(participants))
  397         ? i->date
  398         : base::unixtime::now();
  399     const auto lastActive = (i != end(participants))
  400         ? i->lastActive
  401         : TimeId(0);
  402     const auto volume = (i != end(participants))
  403         ? i->volume
  404         : Group::kDefaultVolume;
  405     const auto canSelfUnmute = (muted() != MuteState::ForceMuted);
  406     const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
  407         | (lastActive ? Flag::f_active_date : Flag(0))
  408         | (_mySsrc ? Flag(0) : Flag::f_left)
  409         | Flag::f_volume // Without flag the volume is reset to 100%.
  410         | Flag::f_volume_by_admin // Self volume can only be set by admin.
  411         | ((muted() != MuteState::Active) ? Flag::f_muted : Flag(0));
  412     call->applyUpdateChecked(
  413         MTP_updateGroupCallParticipants(
  414             inputCall(),
  415             MTP_vector<MTPGroupCallParticipant>(
  416                 1,
  417                 MTP_groupCallParticipant(
  418                     MTP_flags(flags),
  419                     MTP_int(self->bareId()),
  420                     MTP_int(date),
  421                     MTP_int(lastActive),
  422                     MTP_int(_mySsrc),
  423                     MTP_int(volume))),
  424             MTP_int(0)).c_updateGroupCallParticipants());
  425 }
  426 
  427 void GroupCall::applyParticipantLocally(
  428         not_null<UserData*> user,
  429         bool mute,
  430         std::optional<int> volume) {
  431     const auto participant = LookupParticipant(_peer, _id, user);
  432     if (!participant || !participant->ssrc) {
  433         return;
  434     }
  435     const auto canManageCall = _peer->canManageGroupCall();
  436     const auto isMuted = participant->muted || (mute && canManageCall);
  437     const auto canSelfUnmute = !canManageCall
  438         ? participant->canSelfUnmute
  439         : (!mute || IsGroupCallAdmin(_peer, user));
  440     const auto isMutedByYou = mute && !canManageCall;
  441     const auto mutedCount = 0/*participant->mutedCount*/;
  442     using Flag = MTPDgroupCallParticipant::Flag;
  443     const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0))
  444         | Flag::f_volume // Without flag the volume is reset to 100%.
  445         | ((participant->applyVolumeFromMin && !volume)
  446             ? Flag::f_volume_by_admin
  447             : Flag(0))
  448         | (participant->lastActive ? Flag::f_active_date : Flag(0))
  449         | (isMuted ? Flag::f_muted : Flag(0))
  450         | (isMutedByYou ? Flag::f_muted_by_you : Flag(0));
  451     _peer->groupCall()->applyUpdateChecked(
  452         MTP_updateGroupCallParticipants(
  453             inputCall(),
  454             MTP_vector<MTPGroupCallParticipant>(
  455                 1,
  456                 MTP_groupCallParticipant(
  457                     MTP_flags(flags),
  458                     MTP_int(user->bareId()),
  459                     MTP_int(participant->date),
  460                     MTP_int(participant->lastActive),
  461                     MTP_int(participant->ssrc),
  462                     MTP_int(volume.value_or(participant->volume)))),
  463             MTP_int(0)).c_updateGroupCallParticipants());
  464 }
  465 
  466 void GroupCall::hangup() {
  467     finish(FinishType::Ended);
  468 }
  469 
  470 void GroupCall::discard() {
  471     if (!_id) {
  472         _api.request(_createRequestId).cancel();
  473         hangup();
  474         return;
  475     }
  476     _api.request(MTPphone_DiscardGroupCall(
  477         inputCall()
  478     )).done([=](const MTPUpdates &result) {
  479         // Here 'this' could be destroyed by updates, so we set Ended after
  480         // updates being handled, but in a guarded way.
  481         crl::on_main(this, [=] { hangup(); });
  482         _peer->session().api().applyUpdates(result);
  483     }).fail([=](const RPCError &error) {
  484         hangup();
  485     }).send();
  486 }
  487 
  488 void GroupCall::finish(FinishType type) {
  489     Expects(type != FinishType::None);
  490 
  491     const auto finalState = (type == FinishType::Ended)
  492         ? State::Ended
  493         : State::Failed;
  494     const auto hangupState = (type == FinishType::Ended)
  495         ? State::HangingUp
  496         : State::FailedHangingUp;
  497     const auto state = _state.current();
  498     if (state == State::HangingUp
  499         || state == State::FailedHangingUp
  500         || state == State::Ended
  501         || state == State::Failed) {
  502         return;
  503     }
  504     if (!_mySsrc) {
  505         setState(finalState);
  506         return;
  507     }
  508 
  509     setState(hangupState);
  510 
  511     // We want to leave request still being sent and processed even if
  512     // the call is already destroyed.
  513     const auto session = &_peer->session();
  514     const auto weak = base::make_weak(this);
  515     session->api().request(MTPphone_LeaveGroupCall(
  516         inputCall(),
  517         MTP_int(_mySsrc)
  518     )).done([=](const MTPUpdates &result) {
  519         // Here 'this' could be destroyed by updates, so we set Ended after
  520         // updates being handled, but in a guarded way.
  521         crl::on_main(weak, [=] { setState(finalState); });
  522         session->api().applyUpdates(result);
  523     }).fail(crl::guard(weak, [=](const RPCError &error) {
  524         setState(finalState);
  525     })).send();
  526 }
  527 
  528 void GroupCall::setMuted(MuteState mute) {
  529     const auto set = [=] {
  530         const auto wasMuted = (muted() == MuteState::Muted)
  531             || (muted() == MuteState::PushToTalk);
  532         _muted = mute;
  533         const auto nowMuted = (muted() == MuteState::Muted)
  534             || (muted() == MuteState::PushToTalk);
  535         if (wasMuted != nowMuted) {
  536             applySelfInCallLocally();
  537         }
  538     };
  539     if (mute == MuteState::Active || mute == MuteState::PushToTalk) {
  540         _delegate->groupCallRequestPermissionsOrFail(crl::guard(this, set));
  541     } else {
  542         set();
  543     }
  544 }
  545 
  546 void GroupCall::handleUpdate(const MTPGroupCall &call) {
  547     return call.match([&](const MTPDgroupCall &data) {
  548         if (_acceptFields) {
  549             if (!_instance && !_id) {
  550                 join(MTP_inputGroupCall(data.vid(), data.vaccess_hash()));
  551             }
  552             return;
  553         } else if (_id != data.vid().v
  554             || _accessHash != data.vaccess_hash().v
  555             || !_instance) {
  556             return;
  557         }
  558         if (const auto params = data.vparams()) {
  559             params->match([&](const MTPDdataJSON &data) {
  560                 auto error = QJsonParseError{ 0, QJsonParseError::NoError };
  561                 const auto document = QJsonDocument::fromJson(
  562                     data.vdata().v,
  563                     &error);
  564                 if (error.error != QJsonParseError::NoError) {
  565                     LOG(("API Error: "
  566                         "Failed to parse group call params, error: %1."
  567                         ).arg(error.errorString()));
  568                     return;
  569                 } else if (!document.isObject()) {
  570                     LOG(("API Error: "
  571                         "Not an object received in group call params."));
  572                     return;
  573                 }
  574                 const auto readString = [](
  575                         const QJsonObject &object,
  576                         const char *key) {
  577                     return object.value(key).toString().toStdString();
  578                 };
  579                 const auto root = document.object().value("transport").toObject();
  580                 auto payload = tgcalls::GroupJoinResponsePayload();
  581                 payload.ufrag = readString(root, "ufrag");
  582                 payload.pwd = readString(root, "pwd");
  583                 const auto prints = root.value("fingerprints").toArray();
  584                 const auto candidates = root.value("candidates").toArray();
  585                 for (const auto &print : prints) {
  586                     const auto object = print.toObject();
  587                     payload.fingerprints.push_back(tgcalls::GroupJoinPayloadFingerprint{
  588                         .hash = readString(object, "hash"),
  589                         .setup = readString(object, "setup"),
  590                         .fingerprint = readString(object, "fingerprint"),
  591                     });
  592                 }
  593                 for (const auto &candidate : candidates) {
  594                     const auto object = candidate.toObject();
  595                     payload.candidates.push_back(tgcalls::GroupJoinResponseCandidate{
  596                         .port = readString(object, "port"),
  597                         .protocol = readString(object, "protocol"),
  598                         .network = readString(object, "network"),
  599                         .generation = readString(object, "generation"),
  600                         .id = readString(object, "id"),
  601                         .component = readString(object, "component"),
  602                         .foundation = readString(object, "foundation"),
  603                         .priority = readString(object, "priority"),
  604                         .ip = readString(object, "ip"),
  605                         .type = readString(object, "type"),
  606                         .tcpType = readString(object, "tcpType"),
  607                         .relAddr = readString(object, "relAddr"),
  608                         .relPort = readString(object, "relPort"),
  609                     });
  610                 }
  611                 _instance->setJoinResponsePayload(payload);
  612             });
  613         }
  614     }, [&](const MTPDgroupCallDiscarded &data) {
  615         if (data.vid().v == _id) {
  616             _mySsrc = 0;
  617             hangup();
  618         }
  619     });
  620 }
  621 
  622 void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
  623     const auto state = _state.current();
  624     if (state != State::Joined && state != State::Connecting) {
  625         return;
  626     }
  627 
  628     const auto handleOtherParticipants = [=](
  629             const MTPDgroupCallParticipant &data) {
  630         if (data.is_min()) {
  631             // No real information about mutedByMe or my custom volume.
  632             return;
  633         }
  634         const auto user = _peer->owner().user(data.vuser_id().v);
  635         const auto participant = LookupParticipant(_peer, _id, user);
  636         if (!participant) {
  637             return;
  638         }
  639         _otherParticipantStateValue.fire(Group::ParticipantState{
  640             .user = user,
  641             .volume = data.vvolume().value_or_empty(),
  642             .mutedByMe = data.is_muted_by_you(),
  643         });
  644     };
  645 
  646     const auto self = _peer->session().userId();
  647     for (const auto &participant : data.vparticipants().v) {
  648         participant.match([&](const MTPDgroupCallParticipant &data) {
  649             if (data.vuser_id().v != self) {
  650                 handleOtherParticipants(data);
  651                 return;
  652             }
  653             if (data.is_left() && data.vsource().v == _mySsrc) {
  654                 // I was removed from the call, rejoin.
  655                 LOG(("Call Info: Rejoin after got 'left' with my ssrc."));
  656                 setState(State::Joining);
  657                 rejoin();
  658             } else if (!data.is_left() && data.vsource().v != _mySsrc) {
  659                 // I joined from another device, hangup.
  660                 LOG(("Call Info: Hangup after '!left' with ssrc %1, my %2."
  661                     ).arg(data.vsource().v
  662                     ).arg(_mySsrc));
  663                 _mySsrc = 0;
  664                 hangup();
  665             }
  666             if (data.is_muted() && !data.is_can_self_unmute()) {
  667                 setMuted(MuteState::ForceMuted);
  668             } else if (muted() == MuteState::ForceMuted) {
  669                 setMuted(MuteState::Muted);
  670             } else if (data.is_muted() && muted() != MuteState::Muted) {
  671                 setMuted(MuteState::Muted);
  672             }
  673         });
  674     }
  675 }
  676 
  677 void GroupCall::createAndStartController() {
  678     const auto &settings = Core::App().settings();
  679 
  680     const auto weak = base::make_weak(this);
  681     const auto myLevel = std::make_shared<tgcalls::GroupLevelValue>();
  682     tgcalls::GroupInstanceDescriptor descriptor = {
  683         .config = tgcalls::GroupConfig{
  684         },
  685         .networkStateUpdated = [=](bool connected) {
  686             crl::on_main(weak, [=] { setInstanceConnected(connected); });
  687         },
  688         .audioLevelsUpdated = [=](const tgcalls::GroupLevelsUpdate &data) {
  689             const auto &updates = data.updates;
  690             if (updates.empty()) {
  691                 return;
  692             } else if (updates.size() == 1 && !updates.front().ssrc) {
  693                 const auto &value = updates.front().value;
  694                 // Don't send many 0 while we're muted.
  695                 if (myLevel->level == value.level
  696                     && myLevel->voice == value.voice) {
  697                     return;
  698                 }
  699                 *myLevel = updates.front().value;
  700             }
  701             crl::on_main(weak, [=] { audioLevelsUpdated(data); });
  702         },
  703         .initialInputDeviceId = _audioInputId.toStdString(),
  704         .initialOutputDeviceId = _audioOutputId.toStdString(),
  705         .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator(
  706             settings.callAudioBackend()),
  707     };
  708     if (Logs::DebugEnabled()) {
  709         auto callLogFolder = cWorkingDir() + qsl("DebugLogs");
  710         auto callLogPath = callLogFolder + qsl("/last_group_call_log.txt");
  711         auto callLogNative = QDir::toNativeSeparators(callLogPath);
  712 #ifdef Q_OS_WIN
  713         descriptor.config.logPath.data = callLogNative.toStdWString();
  714 #else // Q_OS_WIN
  715         const auto callLogUtf = QFile::encodeName(callLogNative);
  716         descriptor.config.logPath.data.resize(callLogUtf.size());
  717         ranges::copy(callLogUtf, descriptor.config.logPath.data.begin());
  718 #endif // Q_OS_WIN
  719         QFile(callLogPath).remove();
  720         QDir().mkpath(callLogFolder);
  721     }
  722 
  723     LOG(("Call Info: Creating group instance"));
  724     _instance = std::make_unique<tgcalls::GroupInstanceImpl>(
  725         std::move(descriptor));
  726 
  727     updateInstanceMuteState();
  728     updateInstanceVolumes();
  729 
  730     //raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled());
  731 }
  732 
  733 void GroupCall::updateInstanceMuteState() {
  734     Expects(_instance != nullptr);
  735 
  736     const auto state = muted();
  737     _instance->setIsMuted(state != MuteState::Active
  738         && state != MuteState::PushToTalk);
  739 }
  740 
  741 void GroupCall::updateInstanceVolumes() {
  742     const auto real = _peer->groupCall();
  743     if (!real || real->id() != _id) {
  744         return;
  745     }
  746 
  747     const auto &participants = real->participants();
  748     for (const auto &participant : participants) {
  749         const auto setVolume = participant.mutedByMe
  750             || (participant.volume != Group::kDefaultVolume);
  751         if (setVolume && participant.ssrc) {
  752             _instance->setVolume(
  753                 participant.ssrc,
  754                 (participant.mutedByMe
  755                     ? 0.
  756                     : (participant.volume / float64(Group::kDefaultVolume))));
  757         }
  758     }
  759 }
  760 
  761 void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) {
  762     Expects(!data.updates.empty());
  763 
  764     auto check = false;
  765     auto checkNow = false;
  766     const auto now = crl::now();
  767     for (const auto &[ssrcOrZero, value] : data.updates) {
  768         const auto ssrc = ssrcOrZero ? ssrcOrZero : _mySsrc;
  769         const auto level = value.level;
  770         const auto voice = value.voice;
  771         const auto self = (ssrc == _mySsrc);
  772         _levelUpdates.fire(LevelUpdate{
  773             .ssrc = ssrc,
  774             .value = level,
  775             .voice = voice,
  776             .self = self
  777         });
  778         if (level <= kSpeakLevelThreshold) {
  779             continue;
  780         }
  781         if (self
  782             && voice
  783             && (!_lastSendProgressUpdate
  784                 || _lastSendProgressUpdate + kUpdateSendActionEach < now)) {
  785             _lastSendProgressUpdate = now;
  786             _peer->session().sendProgressManager().update(
  787                 _history,
  788                 Api::SendProgressType::Speaking);
  789         }
  790 
  791         check = true;
  792         const auto i = _lastSpoke.find(ssrc);
  793         if (i == _lastSpoke.end()) {
  794             _lastSpoke.emplace(ssrc, Data::LastSpokeTimes{
  795                 .anything = now,
  796                 .voice = voice ? now : 0,
  797             });
  798             checkNow = true;
  799         } else {
  800             if ((i->second.anything + kCheckLastSpokeInterval / 3 <= now)
  801                 || (voice
  802                     && i->second.voice + kCheckLastSpokeInterval / 3 <= now)) {
  803                 checkNow = true;
  804             }
  805             i->second.anything = now;
  806             if (voice) {
  807                 i->second.voice = now;
  808             }
  809         }
  810     }
  811     if (checkNow) {
  812         checkLastSpoke();
  813     } else if (check && !_lastSpokeCheckTimer.isActive()) {
  814         _lastSpokeCheckTimer.callEach(kCheckLastSpokeInterval / 2);
  815     }
  816 }
  817 
  818 void GroupCall::checkLastSpoke() {
  819     const auto real = _peer->groupCall();
  820     if (!real || real->id() != _id) {
  821         return;
  822     }
  823 
  824     auto hasRecent = false;
  825     const auto now = crl::now();
  826     auto list = base::take(_lastSpoke);
  827     for (auto i = list.begin(); i != list.end();) {
  828         const auto [ssrc, when] = *i;
  829         if (when.anything + kCheckLastSpokeInterval >= now) {
  830             hasRecent = true;
  831             ++i;
  832         } else {
  833             i = list.erase(i);
  834         }
  835         real->applyLastSpoke(ssrc, when, now);
  836     }
  837     _lastSpoke = std::move(list);
  838 
  839     if (!hasRecent) {
  840         _lastSpokeCheckTimer.cancel();
  841     } else if (!_lastSpokeCheckTimer.isActive()) {
  842         _lastSpokeCheckTimer.callEach(kCheckLastSpokeInterval / 3);
  843     }
  844 }
  845 
  846 void GroupCall::checkJoined() {
  847     if (state() != State::Connecting || !_id || !_mySsrc) {
  848         return;
  849     }
  850     _api.request(MTPphone_CheckGroupCall(
  851         inputCall(),
  852         MTP_int(_mySsrc)
  853     )).done([=](const MTPBool &result) {
  854         if (!mtpIsTrue(result)) {
  855             LOG(("Call Info: Rejoin after FALSE in checkGroupCall."));
  856             rejoin();
  857         } else if (state() == State::Connecting) {
  858             _checkJoinedTimer.callOnce(kCheckJoinedTimeout);
  859         }
  860     }).fail([=](const RPCError &error) {
  861         LOG(("Call Info: Rejoin after error '%1' in checkGroupCall."
  862             ).arg(error.type()));
  863         rejoin();
  864     }).send();
  865 }
  866 
  867 void GroupCall::setInstanceConnected(bool connected) {
  868     if (_instanceConnected == connected) {
  869         return;
  870     }
  871     _instanceConnected = connected;
  872     if (state() == State::Connecting && connected) {
  873         setState(State::Joined);
  874     } else if (state() == State::Joined && !connected) {
  875         setState(State::Connecting);
  876     }
  877 }
  878 
  879 void GroupCall::maybeSendMutedUpdate(MuteState previous) {
  880     // Send only Active <-> !Active changes.
  881     const auto now = muted();
  882     const auto wasActive = (previous == MuteState::Active);
  883     const auto nowActive = (now == MuteState::Active);
  884     if (now == MuteState::ForceMuted
  885         || previous == MuteState::ForceMuted
  886         || (nowActive == wasActive)) {
  887         return;
  888     }
  889     sendMutedUpdate();
  890 }
  891 
  892 void GroupCall::sendMutedUpdate() {
  893     _api.request(_updateMuteRequestId).cancel();
  894     _updateMuteRequestId = _api.request(MTPphone_EditGroupCallMember(
  895         MTP_flags((muted() != MuteState::Active)
  896             ? MTPphone_EditGroupCallMember::Flag::f_muted
  897             : MTPphone_EditGroupCallMember::Flag(0)),
  898         inputCall(),
  899         MTP_inputUserSelf(),
  900         MTP_int(100000) // volume
  901     )).done([=](const MTPUpdates &result) {
  902         _updateMuteRequestId = 0;
  903         _peer->session().api().applyUpdates(result);
  904     }).fail([=](const RPCError &error) {
  905         _updateMuteRequestId = 0;
  906         if (error.type() == u"GROUPCALL_FORBIDDEN"_q) {
  907             LOG(("Call Info: Rejoin after error '%1' in editGroupCallMember."
  908                 ).arg(error.type()));
  909             rejoin();
  910         }
  911     }).send();
  912 }
  913 
  914 rpl::producer<bool> GroupCall::connectingValue() const {
  915     using namespace rpl::mappers;
  916     return _state.value() | rpl::map(
  917         _1 == State::Creating
  918         || _1 == State::Joining
  919         || _1 == State::Connecting
  920     ) | rpl::distinct_until_changed();
  921 }
  922 
  923 void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) {
  924     if (input) {
  925         _mediaDevices->switchToAudioInput(deviceId);
  926     } else {
  927         _mediaDevices->switchToAudioOutput(deviceId);
  928     }
  929 }
  930 
  931 void GroupCall::toggleMute(const Group::MuteRequest &data) {
  932     if (data.locallyOnly) {
  933         applyParticipantLocally(data.user, data.mute, std::nullopt);
  934     } else {
  935         editParticipant(data.user, data.mute, std::nullopt);
  936     }
  937 }
  938 
  939 void GroupCall::changeVolume(const Group::VolumeRequest &data) {
  940     if (data.locallyOnly) {
  941         applyParticipantLocally(data.user, false, data.volume);
  942     } else {
  943         editParticipant(data.user, false, data.volume);
  944     }
  945 }
  946 
  947 void GroupCall::editParticipant(
  948         not_null<UserData*> user,
  949         bool mute,
  950         std::optional<int> volume) {
  951     const auto participant = LookupParticipant(_peer, _id, user);
  952     if (!participant) {
  953         return;
  954     }
  955     applyParticipantLocally(user, mute, volume);
  956 
  957     using Flag = MTPphone_EditGroupCallMember::Flag;
  958     const auto flags = (mute ? Flag::f_muted : Flag(0))
  959         | (volume.has_value() ? Flag::f_volume : Flag(0));
  960     _api.request(MTPphone_EditGroupCallMember(
  961         MTP_flags(flags),
  962         inputCall(),
  963         user->inputUser,
  964         MTP_int(std::clamp(volume.value_or(0), 1, Group::kMaxVolume))
  965     )).done([=](const MTPUpdates &result) {
  966         _peer->session().api().applyUpdates(result);
  967     }).fail([=](const RPCError &error) {
  968         if (error.type() == u"GROUPCALL_FORBIDDEN"_q) {
  969             LOG(("Call Info: Rejoin after error '%1' in editGroupCallMember."
  970                 ).arg(error.type()));
  971             rejoin();
  972         }
  973     }).send();
  974 }
  975 
  976 std::variant<int, not_null<UserData*>> GroupCall::inviteUsers(
  977         const std::vector<not_null<UserData*>> &users) {
  978     const auto real = _peer->groupCall();
  979     if (!real || real->id() != _id) {
  980         return 0;
  981     }
  982     const auto owner = &_peer->owner();
  983     const auto &invited = owner->invitedToCallUsers(_id);
  984     const auto &participants = real->participants();
  985     auto &&toInvite = users | ranges::view::filter([&](
  986             not_null<UserData*> user) {
  987         return !invited.contains(user) && !ranges::contains(
  988             participants,
  989             user,
  990             &Data::GroupCall::Participant::user);
  991     });
  992 
  993     auto count = 0;
  994     auto slice = QVector<MTPInputUser>();
  995     auto result = std::variant<int, not_null<UserData*>>(0);
  996     slice.reserve(kMaxInvitePerSlice);
  997     const auto sendSlice = [&] {
  998         count += slice.size();
  999         _api.request(MTPphone_InviteToGroupCall(
 1000             inputCall(),
 1001             MTP_vector<MTPInputUser>(slice)
 1002         )).done([=](const MTPUpdates &result) {
 1003             _peer->session().api().applyUpdates(result);
 1004         }).send();
 1005         slice.clear();
 1006     };
 1007     for (const auto user : users) {
 1008         if (!count && slice.empty()) {
 1009             result = user;
 1010         }
 1011         owner->registerInvitedToCallUser(_id, _peer, user);
 1012         slice.push_back(user->inputUser);
 1013         if (slice.size() == kMaxInvitePerSlice) {
 1014             sendSlice();
 1015         }
 1016     }
 1017     if (count != 0 || slice.size() != 1) {
 1018         result = int(count + slice.size());
 1019     }
 1020     if (!slice.empty()) {
 1021         sendSlice();
 1022     }
 1023     return result;
 1024 }
 1025 
 1026 auto GroupCall::ensureGlobalShortcutManager()
 1027 -> std::shared_ptr<GlobalShortcutManager> {
 1028     if (!_shortcutManager) {
 1029         _shortcutManager = base::CreateGlobalShortcutManager();
 1030     }
 1031     return _shortcutManager;
 1032 }
 1033 
 1034 void GroupCall::applyGlobalShortcutChanges() {
 1035     auto &settings = Core::App().settings();
 1036     if (!settings.groupCallPushToTalk()
 1037         || settings.groupCallPushToTalkShortcut().isEmpty()
 1038         || !base::GlobalShortcutsAvailable()
 1039         || !base::GlobalShortcutsAllowed()) {
 1040         _shortcutManager = nullptr;
 1041         _pushToTalk = nullptr;
 1042         return;
 1043     }
 1044     ensureGlobalShortcutManager();
 1045     const auto shortcut = _shortcutManager->shortcutFromSerialized(
 1046         settings.groupCallPushToTalkShortcut());
 1047     if (!shortcut) {
 1048         settings.setGroupCallPushToTalkShortcut(QByteArray());
 1049         settings.setGroupCallPushToTalk(false);
 1050         Core::App().saveSettingsDelayed();
 1051         _shortcutManager = nullptr;
 1052         _pushToTalk = nullptr;
 1053         return;
 1054     }
 1055     if (_pushToTalk) {
 1056         if (shortcut->serialize() == _pushToTalk->serialize()) {
 1057             return;
 1058         }
 1059         _shortcutManager->stopWatching(_pushToTalk);
 1060     }
 1061     _pushToTalk = shortcut;
 1062     _shortcutManager->startWatching(_pushToTalk, [=](bool pressed) {
 1063         pushToTalk(
 1064             pressed,
 1065             Core::App().settings().groupCallPushToTalkDelay());
 1066     });
 1067 }
 1068 
 1069 void GroupCall::pushToTalk(bool pressed, crl::time delay) {
 1070     if (muted() == MuteState::ForceMuted
 1071         || muted() == MuteState::Active) {
 1072         return;
 1073     } else if (pressed) {
 1074         _pushToTalkCancelTimer.cancel();
 1075         setMuted(MuteState::PushToTalk);
 1076     } else if (delay) {
 1077         _pushToTalkCancelTimer.callOnce(delay);
 1078     } else {
 1079         pushToTalkCancel();
 1080     }
 1081 }
 1082 
 1083 void GroupCall::pushToTalkCancel() {
 1084     _pushToTalkCancelTimer.cancel();
 1085     if (muted() == MuteState::PushToTalk) {
 1086         setMuted(MuteState::Muted);
 1087     }
 1088 }
 1089 
 1090 auto GroupCall::otherParticipantStateValue() const
 1091 -> rpl::producer<Group::ParticipantState> {
 1092     return _otherParticipantStateValue.events();
 1093 }
 1094 
 1095 //void GroupCall::setAudioVolume(bool input, float level) {
 1096 //  if (_instance) {
 1097 //      if (input) {
 1098 //          _instance->setInputVolume(level);
 1099 //      } else {
 1100 //          _instance->setOutputVolume(level);
 1101 //      }
 1102 //  }
 1103 //}
 1104 
 1105 void GroupCall::setAudioDuckingEnabled(bool enabled) {
 1106     if (_instance) {
 1107         //_instance->setAudioOutputDuckingEnabled(enabled);
 1108     }
 1109 }
 1110 
 1111 void GroupCall::handleRequestError(const RPCError &error) {
 1112     //if (error.type() == qstr("USER_PRIVACY_RESTRICTED")) {
 1113     //  Ui::show(Box<InformBox>(tr::lng_call_error_not_available(tr::now, lt_user, _user->name)));
 1114     //} else if (error.type() == qstr("PARTICIPANT_VERSION_OUTDATED")) {
 1115     //  Ui::show(Box<InformBox>(tr::lng_call_error_outdated(tr::now, lt_user, _user->name)));
 1116     //} else if (error.type() == qstr("CALL_PROTOCOL_LAYER_INVALID")) {
 1117     //  Ui::show(Box<InformBox>(Lang::Hard::CallErrorIncompatible().replace("{user}", _user->name)));
 1118     //}
 1119     //finish(FinishType::Failed);
 1120 }
 1121 
 1122 void GroupCall::handleControllerError(const QString &error) {
 1123     if (error == u"ERROR_INCOMPATIBLE"_q) {
 1124         //Ui::show(Box<InformBox>(
 1125         //  Lang::Hard::CallErrorIncompatible().replace(
 1126         //      "{user}",
 1127         //      _user->name)));
 1128     } else if (error == u"ERROR_AUDIO_IO"_q) {
 1129         //Ui::show(Box<InformBox>(tr::lng_call_error_audio_io(tr::now)));
 1130     }
 1131     //finish(FinishType::Failed);
 1132 }
 1133 
 1134 MTPInputGroupCall GroupCall::inputCall() const {
 1135     Expects(_id != 0);
 1136 
 1137     return MTP_inputGroupCall(
 1138         MTP_long(_id),
 1139         MTP_long(_accessHash));
 1140 }
 1141 
 1142 void GroupCall::destroyController() {
 1143     if (_instance) {
 1144         //_instance->stop([](tgcalls::FinalState) {
 1145         //});
 1146 
 1147         DEBUG_LOG(("Call Info: Destroying call controller.."));
 1148         _instance.reset();
 1149         DEBUG_LOG(("Call Info: Call controller destroyed."));
 1150     }
 1151 }
 1152 
 1153 } // namespace Calls