From 3a321d64f6c9ea78534a2c54b9ee6e25fb1cebec Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 11 May 2021 13:35:04 +0400 Subject: [PATCH] Switch between videos by left click. --- .../calls/group/calls_group_call.cpp | 100 +++++++------- .../calls/group/calls_group_call.h | 5 +- .../calls/group/calls_group_members.cpp | 124 +++++++++++------- Telegram/SourceFiles/data/data_group_call.cpp | 75 ++++++----- Telegram/SourceFiles/data/data_group_call.h | 3 + 5 files changed, 173 insertions(+), 134 deletions(-) diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 04b68dd8c..47d3f1d6e 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -47,6 +47,7 @@ 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); [[nodiscard]] std::unique_ptr CreateMediaDevices() { const auto &settings = Core::App().settings(); @@ -61,15 +62,9 @@ constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000); uint64 id, not_null participantPeer) { const auto call = peer->groupCall(); - if (!id || !call || call->id() != id) { - return nullptr; - } - const auto &participants = call->participants(); - const auto i = ranges::find( - participants, - participantPeer, - &Data::GroupCallParticipant::peer); - return (i != end(participants)) ? &*i : nullptr; + return (id && call && call->id() == id) + ? call->participantByPeer(participantPeer) + : nullptr; } [[nodiscard]] double TimestampFromMsgId(mtpMsgId msgId) { @@ -559,7 +554,7 @@ void GroupCall::subscribeToReal(not_null real) { newLarge = chooseLargeVideoEndpoint(); } if (_videoEndpointLarge.current() != newLarge) { - _videoEndpointLarge = newLarge; + setVideoEndpointLarge(newLarge); } if (!updateCameraNotStreams.empty()) { _streamsVideoUpdated.fire({ updateCameraNotStreams, false }); @@ -624,7 +619,10 @@ void GroupCall::subscribeToReal(not_null real) { const auto wasSounding = data.was && data.was->sounding; if (nowSpeaking == wasSpeaking && nowSounding == wasSounding) { return; - } else if (_videoEndpointPinned.current()) { + } else if (_videoEndpointPinned.current() + || (_videoLargeShowTime + && _videoLargeShowTime + kFixLargeVideoDuration + > crl::now())) { return; } if (nowScreenEndpoint != newLarge.endpoint @@ -880,7 +878,7 @@ void GroupCall::setMyEndpointType( auto newLarge = _videoEndpointLarge.current(); if (newLarge.endpoint == endpoint) { _videoEndpointPinned = false; - _videoEndpointLarge = chooseLargeVideoEndpoint(); + setVideoEndpointLarge(chooseLargeVideoEndpoint()); } _streamsVideoUpdated.fire({ endpoint, false }); } @@ -899,7 +897,7 @@ void GroupCall::setMyEndpointType( && nowLarge != EndpointType::Screen) || (type == EndpointType::Camera && nowLarge == EndpointType::None))) { - _videoEndpointLarge = VideoEndpoint{ _joinAs, endpoint }; + setVideoEndpointLarge(VideoEndpoint{ _joinAs, endpoint }); } } } @@ -1132,34 +1130,30 @@ void GroupCall::leavePresentation() { } void GroupCall::applyMeInCallLocally() { - const auto call = _peer->groupCall(); - if (!call || call->id() != _id) { + const auto real = lookupReal(); + if (!real) { return; } using Flag = MTPDgroupCallParticipant::Flag; - const auto &participants = call->participants(); - const auto i = ranges::find( - participants, - _joinAs, - &Data::GroupCallParticipant::peer); - const auto date = (i != end(participants)) - ? i->date + const auto participant = real->participantByPeer(_joinAs); + const auto date = participant + ? participant->date : base::unixtime::now(); - const auto lastActive = (i != end(participants)) - ? i->lastActive + const auto lastActive = participant + ? participant->lastActive : TimeId(0); - const auto volume = (i != end(participants)) - ? i->volume + const auto volume = participant + ? participant->volume : Group::kDefaultVolume; const auto canSelfUnmute = (muted() != MuteState::ForceMuted) && (muted() != MuteState::RaisedHand); const auto raisedHandRating = (muted() != MuteState::RaisedHand) ? uint64(0) - : (i != end(participants)) - ? i->raisedHandRating - : FindLocalRaisedHandRating(participants); - const auto params = (i != end(participants)) - ? i->videoParams.get() + : participant + ? participant->raisedHandRating + : FindLocalRaisedHandRating(real->participants()); + const auto params = participant + ? participant->videoParams.get() : nullptr; const auto flags = (canSelfUnmute ? Flag::f_can_self_unmute : Flag(0)) | (lastActive ? Flag::f_active_date : Flag(0)) @@ -1173,7 +1167,7 @@ void GroupCall::applyMeInCallLocally() { ? Flag::f_presentation : Flag(0)) | (raisedHandRating > 0 ? Flag::f_raise_hand_rating : Flag(0)); - call->applyLocalUpdate( + real->applyLocalUpdate( MTP_updateGroupCallParticipants( inputCall(), MTP_vector( @@ -2013,17 +2007,15 @@ bool GroupCall::mediaChannelDescriptionsFill( const auto addVideoChannel = [&]( not_null participantPeer, const auto field) { - const auto i = ranges::find( - existing, - participantPeer, - &Data::GroupCallParticipant::peer); - Assert(i != end(existing)); - Assert(i->videoParams != nullptr); - const auto ¶ms = i->videoParams.get()->*field; + const auto participant = real->participantByPeer( + participantPeer); + Assert(participant != nullptr); + Assert(participant->videoParams != nullptr); + const auto ¶ms = participant->videoParams.get()->*field; Assert(!params.empty()); add(Channel{ .type = Channel::Type::Video, - .audioSsrc = i->ssrc, + .audioSsrc = participant->ssrc, .videoInformation = params.json.toStdString(), }, (field == &ParticipantVideoParams::screen)); }; @@ -2085,7 +2077,7 @@ void GroupCall::setIncomingVideoEndpoints( newLarge = VideoEndpoint(); } if (newLarge.empty()) { - _videoEndpointLarge = chooseLargeVideoEndpoint(); + setVideoEndpointLarge(chooseLargeVideoEndpoint()); } for (const auto &endpoint : removed) { if (_activeVideoEndpoints.contains(endpoint)) { @@ -2134,7 +2126,7 @@ void GroupCall::fillActiveVideoEndpoints() { newLarge = VideoEndpoint(); } if (!newLarge) { - _videoEndpointLarge = chooseLargeVideoEndpoint(); + setVideoEndpointLarge(chooseLargeVideoEndpoint()); } for (const auto &[endpoint, type] : removed) { if (_activeVideoEndpoints.remove(endpoint)) { @@ -2494,16 +2486,30 @@ void GroupCall::sendSelfUpdate(SendUpdateType type) { }).send(); } -void GroupCall::pinVideoEndpoint(const VideoEndpoint &endpoint) { +void GroupCall::pinVideoEndpoint(VideoEndpoint endpoint) { if (!endpoint) { _videoEndpointPinned = false; } else if (streamsVideo(endpoint.endpoint)) { _videoEndpointPinned = false; - _videoEndpointLarge = endpoint; + setVideoEndpointLarge(std::move(endpoint)); _videoEndpointPinned = true; } } +void GroupCall::showVideoEndpointLarge(VideoEndpoint endpoint) { + if (!streamsVideo(endpoint.endpoint)) { + return; + } + _videoEndpointPinned = false; + setVideoEndpointLarge(std::move(endpoint)); + _videoLargeShowTime = crl::now(); +} + +void GroupCall::setVideoEndpointLarge(VideoEndpoint endpoint) { + _videoEndpointLarge = endpoint; + _videoLargeShowTime = 0; +} + void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) { if (input) { _mediaDevices->switchToAudioInput(deviceId); @@ -2572,13 +2578,9 @@ std::variant> GroupCall::inviteUsers( } const auto owner = &_peer->owner(); const auto &invited = owner->invitedToCallUsers(_id); - const auto &participants = real->participants(); auto &&toInvite = users | ranges::views::filter([&]( not_null user) { - return !invited.contains(user) && !ranges::contains( - participants, - user, - &Data::GroupCallParticipant::peer); + return !invited.contains(user) && !real->participantByPeer(user); }); auto count = 0; diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index 03ddc97af..49feff160 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -289,7 +289,7 @@ public: [[nodiscard]] rpl::producer videoEndpointPinnedValue() const { return _videoEndpointPinned.value(); } - void pinVideoEndpoint(const VideoEndpoint &endpoint); + void pinVideoEndpoint(VideoEndpoint endpoint); [[nodiscard]] const VideoEndpoint &videoEndpointLarge() const { return _videoEndpointLarge.current(); } @@ -297,6 +297,7 @@ public: -> rpl::producer { return _videoEndpointLarge.value(); } + void showVideoEndpointLarge(VideoEndpoint endpoint); struct LargeTrack { Webrtc::VideoTrack *track = nullptr; PeerData *peer = nullptr; @@ -451,6 +452,7 @@ private: [[nodiscard]] VideoEndpoint chooseLargeVideoEndpoint() const; [[nodiscard]] EndpointType activeVideoEndpointType( const std::string &endpoint) const; + void setVideoEndpointLarge(VideoEndpoint endpoint); void editParticipant( not_null participantPeer, @@ -539,6 +541,7 @@ private: rpl::variable _videoEndpointPinned; std::unique_ptr _videoLargeTrackWrap; rpl::variable _videoLargeTrack; + crl::time _videoLargeShowTime = 0; base::flat_map _lastSpoke; rpl::event_stream _rejoinEvents; rpl::event_stream<> _allowedToSpeakNotifications; diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index 5dd8688d2..304da6f71 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -159,6 +159,8 @@ private: void setRowVideoEndpoint( not_null row, const std::string &endpoint); + bool toggleRowVideo(not_null row); + void showRowMenu(not_null row); void generateNarrowShadow(); void appendInvitedUsers(); @@ -377,16 +379,13 @@ void Members::Controller::setupListChangeViewers() { const auto row = i->second; const auto real = _call->lookupReal(); Assert(real != nullptr); - const auto &participants = real->participants(); - const auto j = ranges::find( - participants, - row->peer(), - &Data::GroupCallParticipant::peer); - if (j == end(participants)) { + const auto participant = real->participantByPeer( + row->peer()); + if (!participant) { setRowVideoEndpoint(row, std::string()); } else { - const auto &camera = computeCameraEndpoint(&*j); - const auto &screen = computeScreenEndpoint(&*j); + const auto &camera = computeCameraEndpoint(participant); + const auto &screen = computeScreenEndpoint(participant); if (update.endpoint == camera && (_largeEndpoint != screen) && _call->streamsVideo(screen)) { @@ -730,12 +729,7 @@ const Data::GroupCallParticipant *Members::Controller::findParticipant( return nullptr; } else if (endpoint == _call->screenSharingEndpoint() || endpoint == _call->cameraSharingEndpoint()) { - const auto &participants = real->participants(); - const auto i = ranges::find( - participants, - _call->joinAs(), - &Data::GroupCallParticipant::peer); - return (i != end(participants)) ? &*i : nullptr; + return real->participantByPeer(_call->joinAs()); } else { return real->participantByEndpoint(endpoint); } @@ -784,7 +778,6 @@ bool Members::Controller::isMe(not_null participantPeer) const { void Members::Controller::prepareRows(not_null real) { auto foundMe = false; auto changed = false; - const auto &participants = real->participants(); auto count = delegate()->peerListFullRowsCount(); for (auto i = 0; i != count;) { auto row = delegate()->peerListRowAt(i); @@ -794,11 +787,7 @@ void Members::Controller::prepareRows(not_null real) { ++i; continue; } - const auto contains = ranges::contains( - participants, - participantPeer, - &Data::GroupCallParticipant::peer); - if (contains) { + if (real->participantByPeer(participantPeer)) { ++i; } else { changed = true; @@ -808,19 +797,16 @@ void Members::Controller::prepareRows(not_null real) { } if (!foundMe) { const auto me = _call->joinAs(); - const auto i = ranges::find( - participants, - me, - &Data::GroupCallParticipant::peer); - auto row = (i != end(participants)) - ? createRow(*i) + const auto participant = real->participantByPeer(me); + auto row = participant + ? createRow(*participant) : createRowForMe(); if (row) { changed = true; delegate()->peerListAppendRow(std::move(row)); } } - for (const auto &participant : participants) { + for (const auto &participant : real->participants()) { if (auto row = createRow(participant)) { changed = true; delegate()->peerListAppendRow(std::move(row)); @@ -1089,6 +1075,12 @@ auto Members::Controller::kickParticipantRequests() const } void Members::Controller::rowClicked(not_null row) { + if (!toggleRowVideo(row)) { + showRowMenu(row); + } +} + +void Members::Controller::showRowMenu(not_null row) { delegate()->peerListShowRowMenu(row, [=](not_null menu) { if (!_menu || _menu.get() != menu) { return; @@ -1103,9 +1095,51 @@ void Members::Controller::rowClicked(not_null row) { }); } +bool Members::Controller::toggleRowVideo(not_null row) { + const auto real = _call->lookupReal(); + if (!real) { + return false; + } + const auto participantPeer = row->peer(); + const auto isMe = (participantPeer == _call->joinAs()); + const auto participant = real->participantByPeer(participantPeer); + if (!participant) { + return false; + } + const auto params = participant->videoParams.get(); + const auto empty = std::string(); + const auto &camera = isMe + ? _call->cameraSharingEndpoint() + : (params && _call->streamsVideo(params->camera.endpoint)) + ? params->camera.endpoint + : empty; + const auto &screen = isMe + ? _call->screenSharingEndpoint() + : (params && _call->streamsVideo(params->screen.endpoint)) + ? params->screen.endpoint + : empty; + const auto &large = _call->videoEndpointLarge().endpoint; + const auto show = [&] { + if (!screen.empty() && large != screen) { + return screen; + } else if (!camera.empty() && large != camera) { + return camera; + } + return std::string(); + }(); + if (show.empty()) { + return false; + } else if (_call->videoEndpointPinned()) { + _call->pinVideoEndpoint({ participantPeer, show }); + } else { + _call->showVideoEndpointLarge({ participantPeer, show }); + } + return true; +} + void Members::Controller::rowActionClicked( not_null row) { - rowClicked(row); + showRowMenu(row); } base::unique_qptr Members::Controller::rowContextMenu( @@ -1195,26 +1229,20 @@ base::unique_qptr Members::Controller::createRowContextMenu( result->addAction( tr::lng_group_call_context_unpin_camera(tr::now), [=] { _call->pinVideoEndpoint(VideoEndpoint()); }); - } else { - const auto &participants = real->participants(); - const auto i = ranges::find( - participants, - participantPeer, - &Data::GroupCallParticipant::peer); - if (i != end(participants)) { - const auto &camera = computeCameraEndpoint(&*i); - const auto &screen = computeScreenEndpoint(&*i); - const auto streamsScreen = _call->streamsVideo(screen); - if (streamsScreen || _call->streamsVideo(camera)) { - const auto callback = [=] { - _call->pinVideoEndpoint(VideoEndpoint{ - participantPeer, - streamsScreen ? screen : camera }); - }; - result->addAction( - tr::lng_group_call_context_pin_camera(tr::now), - callback); - } + } else if (const auto participant = real->participantByPeer( + participantPeer)) { + const auto &camera = computeCameraEndpoint(participant); + const auto &screen = computeScreenEndpoint(participant); + const auto streamsScreen = _call->streamsVideo(screen); + if (streamsScreen || _call->streamsVideo(camera)) { + const auto callback = [=] { + _call->pinVideoEndpoint(VideoEndpoint{ + participantPeer, + streamsScreen ? screen : camera }); + }; + result->addAction( + tr::lng_group_call_context_pin_camera(tr::now), + callback); } } } diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index aec5a269b..132520295 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -229,6 +229,17 @@ PeerData *GroupCall::participantPeerByScreenSsrc(uint32 ssrc) const { : nullptr; } +const GroupCallParticipant *GroupCall::participantByPeer( + not_null peer) const { + return const_cast(this)->findParticipant(peer); +} + +GroupCallParticipant *GroupCall::findParticipant( + not_null peer) { + const auto i = ranges::find(_participants, peer, &Participant::peer); + return (i != end(_participants)) ? &*i : nullptr; +} + const GroupCallParticipant *GroupCall::participantByEndpoint( const std::string &endpoint) const { if (endpoint.empty()) { @@ -683,24 +694,22 @@ void GroupCall::applyLastSpoke( requestUnknownParticipants(); return; } - const auto j = ranges::find( - _participants, - i->second, - &Participant::peer); - Assert(j != end(_participants)); + const auto participant = findParticipant(i->second); + Assert(participant != nullptr); - _speakingByActiveFinishes.remove(j->peer); + _speakingByActiveFinishes.remove(participant->peer); const auto sounding = (when.anything + kSoundStatusKeptFor >= now) - && j->canSelfUnmute; + && participant->canSelfUnmute; const auto speaking = sounding && (when.voice + kSoundStatusKeptFor >= now); - if (j->sounding != sounding || j->speaking != speaking) { - const auto was = *j; - j->sounding = sounding; - j->speaking = speaking; + if (participant->sounding != sounding + || participant->speaking != speaking) { + const auto was = *participant; + participant->sounding = sounding; + participant->speaking = speaking; _participantUpdates.fire({ .was = was, - .now = *j, + .now = *participant, }); } } @@ -722,41 +731,37 @@ void GroupCall::applyActiveUpdate( if (inCall()) { return; } - const auto i = participantPeerLoaded - ? ranges::find( - _participants, - not_null{ participantPeerLoaded }, - &Participant::peer) - : _participants.end(); - const auto notFound = (i == end(_participants)); - const auto loadByUserId = notFound || i->onlyMinLoaded; + const auto participant = participantPeerLoaded + ? findParticipant(participantPeerLoaded) + : nullptr; + const auto loadByUserId = !participant || participant->onlyMinLoaded; if (loadByUserId) { _unknownSpokenPeerIds[participantPeerId] = when; requestUnknownParticipants(); } - if (notFound || !i->canSelfUnmute) { + if (!participant || !participant->canSelfUnmute) { return; } - const auto was = std::make_optional(*i); + const auto was = std::make_optional(*participant); const auto now = crl::now(); const auto elapsed = TimeId((now - when.anything) / crl::time(1000)); const auto lastActive = base::unixtime::now() - elapsed; const auto finishes = when.anything + kSpeakingAfterActive; - if (lastActive <= i->lastActive || finishes <= now) { + if (lastActive <= participant->lastActive || finishes <= now) { return; } - _speakingByActiveFinishes[i->peer] = finishes; + _speakingByActiveFinishes[participant->peer] = finishes; if (!_speakingByActiveFinishTimer.isActive()) { _speakingByActiveFinishTimer.callOnce(finishes - now); } - i->lastActive = lastActive; - i->speaking = true; - i->canSelfUnmute = true; + participant->lastActive = lastActive; + participant->speaking = true; + participant->canSelfUnmute = true; if (!was->speaking || !was->canSelfUnmute) { _participantUpdates.fire({ .was = was, - .now = *i, + .now = *participant, }); } } @@ -779,16 +784,14 @@ void GroupCall::checkFinishSpeakingByActive() { } } for (const auto participantPeer : stop) { - const auto i = ranges::find( - _participants, - participantPeer, - &Participant::peer); - if (i->speaking) { - const auto was = *i; - i->speaking = false; + const auto participant = findParticipant(participantPeer); + Assert(participant != nullptr); + if (participant->speaking) { + const auto was = *participant; + participant->speaking = false; _participantUpdates.fire({ .was = was, - .now = *i, + .now = *participant, }); } } diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index e0eba0607..2aa0f69f6 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -108,6 +108,8 @@ public: [[nodiscard]] PeerData *participantPeerByAudioSsrc(uint32 ssrc) const; [[nodiscard]] PeerData *participantPeerByCameraSsrc(uint32 ssrc) const; [[nodiscard]] PeerData *participantPeerByScreenSsrc(uint32 ssrc) const; + [[nodiscard]] const Participant *participantByPeer( + not_null peer) const; [[nodiscard]] const Participant *participantByEndpoint( const std::string &endpoint) const; @@ -178,6 +180,7 @@ private: const MTPphone_GroupCall &call) const; [[nodiscard]] bool processSavedFullCall(); void finishParticipantsSliceRequest(); + [[nodiscard]] Participant *findParticipant(not_null peer); void emplaceVideoSsrcs(const Participant &participant); void eraseVideoSsrcs(const Participant &participant);