diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 0d9bde1c3b..aa6d15059c 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -29,16 +29,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "webrtc/webrtc_media_devices.h" #include "webrtc/webrtc_create_adm.h" -#include +#include #include #include #include -namespace tgcalls { -class GroupInstanceImpl; -} // namespace tgcalls - namespace Calls { namespace { @@ -304,6 +300,8 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) { } }, _lifetime); + addParticipantsToInstance(); + SubscribeToMigration(_peer, _lifetime, [=](not_null group) { _peer = group; }); @@ -636,7 +634,12 @@ void GroupCall::handleUpdate(const MTPGroupCall &call) { .relPort = readString(object, "relPort"), }); } - _instance->setJoinResponsePayload(payload); + _instance->setConnectionMode( + tgcalls::GroupConnectionMode::GroupConnectionModeRtc); + _instance->setJoinResponsePayload(payload, {}); + _instancePayloadsDone = true; + + addParticipantsToInstance(); }); } }, [&](const MTPDgroupCallDiscarded &data) { @@ -647,6 +650,45 @@ void GroupCall::handleUpdate(const MTPGroupCall &call) { }); } +void GroupCall::addParticipantsToInstance() { + const auto real = _peer->groupCall(); + if (!real || (real->id() != _id) || !_instancePayloadsDone) { + return; + } + for (const auto &participant : real->participants()) { + prepareParticipantForAdding(participant); + } + addPreparedParticipants(); +} + +void GroupCall::prepareParticipantForAdding( + const Data::GroupCallParticipant &participant) { + _preparedParticipants.push_back(tgcalls::GroupParticipantDescription()); + auto &added = _preparedParticipants.back(); + added.audioSsrc = participant.ssrc; + _unresolvedSsrcs.remove(added.audioSsrc); +} + +void GroupCall::addPreparedParticipants() { + _addPreparedParticipantsScheduled = false; + if (!_preparedParticipants.empty()) { + _instance->addParticipants(base::take(_preparedParticipants)); + } + if (const auto real = _peer->groupCall(); real && real->id() == _id) { + if (!_unresolvedSsrcs.empty()) { + real->resolveParticipants(base::take(_unresolvedSsrcs)); + } + } +} + +void GroupCall::addPreparedParticipantsDelayed() { + if (_addPreparedParticipantsScheduled) { + return; + } + _addPreparedParticipantsScheduled = true; + crl::on_main(this, [=] { addPreparedParticipants(); }); +} + void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) { const auto state = _state.current(); if (state != State::Joined && state != State::Connecting) { @@ -751,6 +793,12 @@ void GroupCall::createAndStartController() { .initialOutputDeviceId = _audioOutputId.toStdString(), .createAudioDeviceModule = Webrtc::AudioDeviceModuleCreator( settings.callAudioBackend()), + .participantDescriptionsRequired = [=]( + const std::vector &ssrcs) { + crl::on_main(weak, [=] { + requestParticipantsInformation(ssrcs); + }); + }, }; if (Logs::DebugEnabled()) { auto callLogFolder = cWorkingDir() + qsl("DebugLogs"); @@ -768,7 +816,8 @@ void GroupCall::createAndStartController() { } LOG(("Call Info: Creating group instance")); - _instance = std::make_unique( + _instancePayloadsDone = false; + _instance = std::make_unique( std::move(descriptor)); updateInstanceMuteState(); @@ -777,6 +826,34 @@ void GroupCall::createAndStartController() { //raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled()); } +void GroupCall::requestParticipantsInformation( + const std::vector &ssrcs) { + const auto real = _peer->groupCall(); + if (!real || real->id() != _id || !_instancePayloadsDone) { + for (const auto ssrc : ssrcs) { + _unresolvedSsrcs.emplace(ssrc); + } + return; + } + + const auto &existing = real->participants(); + for (const auto ssrc : ssrcs) { + const auto participantPeer = real->participantPeerBySsrc(ssrc); + if (!participantPeer) { + _unresolvedSsrcs.emplace(ssrc); + continue; + } + const auto i = ranges::find( + existing, + not_null{ participantPeer }, + &Data::GroupCall::Participant::peer); + Assert(i != end(existing)); + + prepareParticipantForAdding(*i); + } + addPreparedParticipants(); +} + void GroupCall::updateInstanceMuteState() { Expects(_instance != nullptr); diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 2ed5f8c325..68a6d6a073 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -16,8 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; namespace tgcalls { -class GroupInstanceImpl; +class GroupInstanceCustomImpl; struct GroupLevelsUpdate; +struct GroupParticipantDescription; } // namespace tgcalls namespace base { @@ -31,6 +32,7 @@ class MediaDevices; namespace Data { struct LastSpokeTimes; +struct GroupCallParticipant; } // namespace Data namespace Calls { @@ -207,6 +209,13 @@ private: void stopConnectingSound(); void playConnectingSoundOnce(); + void requestParticipantsInformation(const std::vector &ssrcs); + void addParticipantsToInstance(); + void prepareParticipantForAdding( + const Data::GroupCallParticipant &participant); + void addPreparedParticipants(); + void addPreparedParticipantsDelayed(); + void editParticipant( not_null participantPeer, bool mute, @@ -224,6 +233,10 @@ private: MTP::Sender _api; rpl::variable _state = State::Creating; bool _instanceConnected = false; + bool _instancePayloadsDone = false; + base::flat_set _unresolvedSsrcs; + std::vector _preparedParticipants; + bool _addPreparedParticipantsScheduled = false; not_null _joinAs; std::vector> _possibleJoinAs; @@ -239,7 +252,7 @@ private: mtpRequestId _createRequestId = 0; mtpRequestId _updateMuteRequestId = 0; - std::unique_ptr _instance; + std::unique_ptr _instance; rpl::event_stream _levelUpdates; base::flat_map _lastSpoke; rpl::event_stream _rejoinEvents; diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 238982f8e9..baeb26fb19 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -378,6 +378,16 @@ void GroupCall::applyLastSpoke( } } +void GroupCall::resolveParticipants(const base::flat_set &ssrcs) { + if (ssrcs.empty()) { + return; + } + for (const auto ssrc : ssrcs) { + _unknownSpokenSsrcs.emplace(ssrc, LastSpokeTimes()); + } + requestUnknownParticipants(); +} + void GroupCall::applyActiveUpdate( PeerId participantPeerId, LastSpokeTimes when, @@ -531,7 +541,9 @@ void GroupCall::requestUnknownParticipants() { _unknownParticipantPeersRequestId = 0; const auto now = crl::now(); for (const auto [ssrc, when] : ssrcs) { - applyLastSpoke(ssrc, when, now); + if (when.voice || when.anything) { + applyLastSpoke(ssrc, when, now); + } _unknownSpokenSsrcs.remove(ssrc); } for (const auto [id, when] : participantPeerIds) { diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index f353e4c0e4..854710c41d 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -20,6 +20,21 @@ struct LastSpokeTimes { crl::time voice = 0; }; +struct GroupCallParticipant { + not_null peer; + TimeId date = 0; + TimeId lastActive = 0; + uint32 ssrc = 0; + int volume = 0; + bool applyVolumeFromMin = true; + bool sounding = false; + bool speaking = false; + bool muted = false; + bool mutedByMe = false; + bool canSelfUnmute = false; + bool onlyMinLoaded = false; +}; + class GroupCall final { public: GroupCall(not_null peer, uint64 id, uint64 accessHash); @@ -40,20 +55,7 @@ public: void setPeer(not_null peer); - struct Participant { - not_null peer; - TimeId date = 0; - TimeId lastActive = 0; - uint32 ssrc = 0; - int volume = 0; - bool applyVolumeFromMin = true; - bool sounding = false; - bool speaking = false; - bool muted = false; - bool mutedByMe = false; - bool canSelfUnmute = false; - bool onlyMinLoaded = false; - }; + using Participant = GroupCallParticipant; struct ParticipantUpdate { std::optional was; std::optional now; @@ -65,6 +67,7 @@ public: -> const std::vector &; void requestParticipants(); [[nodiscard]] bool participantsLoaded() const; + [[nodiscard]] PeerData *participantPeerBySsrc(uint32 ssrc) const; [[nodiscard]] rpl::producer<> participantsSliceAdded(); [[nodiscard]] rpl::producer participantUpdated() const; @@ -79,6 +82,8 @@ public: LastSpokeTimes when, PeerData *participantPeerLoaded); + void resolveParticipants(const base::flat_set &ssrcs); + [[nodiscard]] int fullCount() const; [[nodiscard]] rpl::producer fullCountValue() const; @@ -105,7 +110,6 @@ private: void requestUnknownParticipants(); void changePeerEmptyCallFlag(); void checkFinishSpeakingByActive(); - [[nodiscard]] PeerData *participantPeerBySsrc(uint32 ssrc) const; const uint64 _id = 0; const uint64 _accessHash = 0; diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index 7a7e68ed0f..d21dd0b007 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -47,6 +47,8 @@ PRIVATE Message.h NetworkManager.cpp NetworkManager.h + StaticThreads.cpp + StaticThreads.h ThreadLocalObject.h VideoCaptureInterface.cpp VideoCaptureInterface.h @@ -54,8 +56,12 @@ PRIVATE VideoCaptureInterfaceImpl.h VideoCapturerInterface.h - group/GroupInstanceImpl.cpp - group/GroupInstanceImpl.h + group/GroupInstanceCustomImpl.cpp + group/GroupInstanceCustomImpl.h + group/GroupNetworkManager.cpp + group/GroupNetworkManager.h + group/StreamingPart.cpp + group/StreamingPart.h platform/PlatformInterface.h @@ -119,6 +125,7 @@ PRIVATE target_link_libraries(lib_tgcalls PRIVATE desktop-app::external_webrtc + desktop-app::external_opus ) target_compile_definitions(lib_tgcalls