From 6a001f2e6c0ba66fdcf1d54977aa0d8971c5af7b Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 30 May 2021 23:52:01 +0400 Subject: [PATCH] Auto-switch large video by speaking participant. --- .../calls/group/calls_group_call.cpp | 48 +++++++++++++++++-- .../calls/group/calls_group_call.h | 2 +- Telegram/SourceFiles/data/data_group_call.cpp | 8 ++++ Telegram/SourceFiles/data/data_group_call.h | 8 +++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index d1d13dafb..4f5de9f22 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -48,7 +48,8 @@ constexpr auto kCheckLastSpokeInterval = crl::time(1000); constexpr auto kCheckJoinedTimeout = 4 * crl::time(1000); constexpr auto kUpdateSendActionEach = crl::time(500); constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000); -constexpr auto kFixLargeVideoDuration = 5 * crl::time(1000); +constexpr auto kFixManualLargeVideoDuration = 5 * crl::time(1000); +constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000); [[nodiscard]] std::unique_ptr CreateMediaDevices() { const auto &settings = Core::App().settings(); @@ -629,6 +630,40 @@ void GroupCall::subscribeToReal(not_null real) { return ssrcs->contains(ssrc); }); }, _lifetime); + + real->participantSpeaking( + ) | rpl::filter([=] { + return _videoEndpointLarge.current(); + }) | rpl::start_with_next([=](not_null p) { + const auto now = crl::now(); + if (_videoEndpointLarge.current().peer == p->peer) { + _videoLargeTillTime = std::max( + _videoLargeTillTime, + now + kFixSpeakingLargeVideoDuration); + return; + } else if (videoEndpointPinned() || _videoLargeTillTime > now) { + return; + } + using Type = VideoEndpointType; + if (p->cameraEndpoint().empty() && p->screenEndpoint().empty()) { + return; + } + const auto tryEndpoint = [&](Type type, const std::string &id) { + if (id.empty()) { + return false; + } + const auto endpoint = VideoEndpoint{ type, p->peer, id }; + if (!shownVideoTracks().contains(endpoint)) { + return false; + } + setVideoEndpointLarge(endpoint); + return true; + }; + if (tryEndpoint(Type::Screen, p->screenEndpoint()) + || tryEndpoint(Type::Camera, p->cameraEndpoint())) { + _videoLargeTillTime = now + kFixSpeakingLargeVideoDuration; + } + }, _lifetime); } void GroupCall::checkGlobalShortcutAvailability() { @@ -2335,12 +2370,16 @@ void GroupCall::checkLastSpoke() { return; } + constexpr auto kKeepInListFor = kCheckLastSpokeInterval * 2; + static_assert(Data::GroupCall::kSoundStatusKeptFor + <= kKeepInListFor - (kCheckLastSpokeInterval / 3)); + auto hasRecent = false; const auto now = crl::now(); auto list = base::take(_lastSpoke); for (auto i = list.begin(); i != list.end();) { const auto [ssrc, when] = *i; - if (when.anything + kCheckLastSpokeInterval >= now) { + if (when.anything + kKeepInListFor >= now) { hasRecent = true; ++i; } else { @@ -2551,9 +2590,12 @@ void GroupCall::pinVideoEndpoint(VideoEndpoint endpoint) { } void GroupCall::showVideoEndpointLarge(VideoEndpoint endpoint) { + if (_videoEndpointLarge.current() == endpoint) { + return; + } _videoEndpointPinned = false; setVideoEndpointLarge(std::move(endpoint)); - _videoLargeShowTime = crl::now(); + _videoLargeTillTime = crl::now() + kFixManualLargeVideoDuration; } void GroupCall::setVideoEndpointLarge(VideoEndpoint endpoint) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index 6d3c9eb7b..93abacc67 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -592,7 +592,7 @@ private: base::flat_set _shownVideoTracks; rpl::variable _videoEndpointLarge; rpl::variable _videoEndpointPinned = false; - crl::time _videoLargeShowTime = 0; + crl::time _videoLargeTillTime = 0; base::flat_map _lastSpoke; rpl::event_stream _rejoinEvents; rpl::event_stream<> _allowedToSpeakNotifications; diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 7aee12714..b06e40cc2 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -251,6 +251,11 @@ auto GroupCall::participantUpdated() const return _participantUpdates.events(); } +auto GroupCall::participantSpeaking() const +-> rpl::producer> { + return _participantSpeaking.events(); +} + void GroupCall::enqueueUpdate(const MTPUpdate &update) { update.match([&](const MTPDupdateGroupCall &updateData) { updateData.vcall().match([&](const MTPDgroupCall &data) { @@ -658,6 +663,9 @@ void GroupCall::applyLastSpoke( && participant->canSelfUnmute; const auto speaking = sounding && (when.voice + kSoundStatusKeptFor >= now); + if (speaking) { + _participantSpeaking.fire({ participant }); + } if (participant->sounding != sounding || participant->speaking != speaking) { const auto was = *participant; diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 8fe688e97..ee1451156 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -105,7 +105,7 @@ public: std::optional now; }; - static constexpr auto kSoundStatusKeptFor = crl::time(1350); + static constexpr auto kSoundStatusKeptFor = crl::time(1500); [[nodiscard]] auto participants() const -> const std::vector &; @@ -118,7 +118,10 @@ public: const std::string &endpoint) const; [[nodiscard]] rpl::producer<> participantsReloaded(); - [[nodiscard]] rpl::producer participantUpdated() const; + [[nodiscard]] auto participantUpdated() const + -> rpl::producer; + [[nodiscard]] auto participantSpeaking() const + -> rpl::producer>; void enqueueUpdate(const MTPUpdate &update); void applyLocalUpdate( @@ -222,6 +225,7 @@ private: mtpRequestId _unknownParticipantPeersRequestId = 0; rpl::event_stream _participantUpdates; + rpl::event_stream> _participantSpeaking; rpl::event_stream<> _participantsReloaded; bool _joinMuted = false;