diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index f002f3cd220a44..d540c62b6d8094 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -3570,6 +3570,24 @@ float64 GroupCall::singleSourceVolumeValue() const { return _singleSourceVolume / float64(Group::kDefaultVolume); } +float64 GroupCall::effectiveParticipantVolume( + const Data::GroupCallParticipant &participant) const { + return participant.mutedByMe + ? 0. + : (participant.volume / float64(Group::kDefaultVolume)); +} + +float64 GroupCall::effectiveParticipantScreenVolume( + const Data::GroupCallParticipant &participant) const { + const auto i = _screenVolumesByPeer.find(participant.peer); + if (i != end(_screenVolumesByPeer)) { + return i->second.muted + ? 0. + : (i->second.volume / float64(Group::kDefaultVolume)); + } + return effectiveParticipantVolume(participant); +} + void GroupCall::updateInstanceVolumes() { const auto real = lookupReal(); if (!real) { @@ -3606,14 +3624,13 @@ void GroupCall::updateInstanceVolume( && (volumeChanged || (was && (GetAdditionalAudioSsrc(was->videoParams) != additionalSsrc))); - const auto localVolume = now.mutedByMe - ? 0. - : (now.volume / float64(Group::kDefaultVolume)); + const auto localVolume = effectiveParticipantVolume(now); + const auto localScreenVolume = effectiveParticipantScreenVolume(now); if (set) { _instance->setVolume(now.ssrc, localVolume); } if (additionalSet) { - _instance->setVolume(additionalSsrc, localVolume); + _instance->setVolume(additionalSsrc, localScreenVolume); } } @@ -3996,6 +4013,42 @@ void GroupCall::changeVolume(const Group::VolumeRequest &data) { } } +void GroupCall::toggleScreenMute(const Group::MuteRequest &data) { + if (_rtmp || videoStream() || data.peer->isSelf()) { + return; + } + auto &state = _screenVolumesByPeer[data.peer]; + state.muted = data.mute; + _otherParticipantScreenStateValue.fire(participantScreenState(data.peer)); + applyScreenVolume(data.peer); +} + +void GroupCall::changeScreenVolume(const Group::VolumeRequest &data) { + if (_rtmp || videoStream() || data.peer->isSelf()) { + return; + } + auto &state = _screenVolumesByPeer[data.peer]; + state.volume = std::clamp(data.volume, 1, Group::kMaxVolume); + state.muted = false; + _otherParticipantScreenStateValue.fire(participantScreenState(data.peer)); + applyScreenVolume(data.peer); +} + +void GroupCall::applyScreenVolume(not_null participantPeer) { + if (!_instance) { + return; + } + const auto participant = LookupParticipant(this, participantPeer); + if (!participant) { + return; + } + const auto ssrc = GetAdditionalAudioSsrc(participant->videoParams); + if (!ssrc) { + return; + } + _instance->setVolume(ssrc, effectiveParticipantScreenVolume(*participant)); +} + void GroupCall::editParticipant( not_null participantPeer, bool mute, @@ -4222,6 +4275,40 @@ auto GroupCall::otherParticipantStateValue() const return _otherParticipantStateValue.events(); } +auto GroupCall::otherParticipantScreenStateValue() const +-> rpl::producer { + return _otherParticipantScreenStateValue.events(); +} + +bool GroupCall::hasScreenShareAudio( + not_null participantPeer) { + const auto participant = LookupParticipant(this, participantPeer); + return participant && GetAdditionalAudioSsrc(participant->videoParams); +} + +Group::ParticipantState GroupCall::participantScreenState( + not_null participantPeer) { + const auto participant = LookupParticipant(this, participantPeer); + const auto i = _screenVolumesByPeer.find(participantPeer); + const auto fallbackVolume = participant + ? participant->volume + : Group::kDefaultVolume; + const auto fallbackMuted = participant ? participant->mutedByMe : false; + return Group::ParticipantState{ + .peer = participantPeer, + .volume = std::clamp( + (i != end(_screenVolumesByPeer)) + ? i->second.volume + : fallbackVolume, + 1, + Group::kMaxVolume), + .mutedByMe = (i != end(_screenVolumesByPeer)) + ? i->second.muted + : fallbackMuted, + .locallyOnly = true, + }; +} + MTPInputGroupCall GroupCall::inputCall() const { Expects(_id != 0); diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index c2820cd8b7d0e3..c6d97364b29754 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -10,6 +10,7 @@ For license and copyright information please follow this link: #include "base/weak_ptr.h" #include "base/timer.h" #include "base/bytes.h" +#include "calls/group/calls_group_common.h" #include "mtproto/sender.h" #include "mtproto/mtproto_auth_key.h" #include "webrtc/webrtc_device_common.h" @@ -338,6 +339,12 @@ class GroupCall final [[nodiscard]] auto otherParticipantStateValue() const -> rpl::producer; + [[nodiscard]] auto otherParticipantScreenStateValue() const + -> rpl::producer; + [[nodiscard]] bool hasScreenShareAudio( + not_null participantPeer); + [[nodiscard]] Group::ParticipantState participantScreenState( + not_null participantPeer); enum State { Creating, @@ -454,6 +461,8 @@ class GroupCall final void toggleMute(const Group::MuteRequest &data); void changeVolume(const Group::VolumeRequest &data); + void toggleScreenMute(const Group::MuteRequest &data); + void changeScreenVolume(const Group::VolumeRequest &data); void inviteUsers( const std::vector &requests, @@ -594,6 +603,11 @@ class GroupCall final void updateInstanceVolume( const std::optional &was, const Data::GroupCallParticipant &now); + [[nodiscard]] float64 effectiveParticipantVolume( + const Data::GroupCallParticipant &participant) const; + [[nodiscard]] float64 effectiveParticipantScreenVolume( + const Data::GroupCallParticipant &participant) const; + void applyScreenVolume(not_null participantPeer); void applyMeInCallLocally(); void startRejoin(); void rejoin(); @@ -730,7 +744,13 @@ class GroupCall final bool _acceptFields = false; rpl::event_stream _otherParticipantStateValue; + rpl::event_stream _otherParticipantScreenStateValue; std::vector _queuedSelfUpdates; + struct ScreenVolumeState { + int volume = Group::kDefaultVolume; + bool muted = false; + }; + base::flat_map, ScreenVolumeState> _screenVolumesByPeer; CallId _id = 0; CallId _accessHash = 0; diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index c3e4328e90063f..d0d3dc807759ac 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -80,6 +80,8 @@ class Members::Controller final } [[nodiscard]] rpl::producer toggleMuteRequests() const; [[nodiscard]] rpl::producer changeVolumeRequests() const; + [[nodiscard]] rpl::producer toggleScreenMuteRequests() const; + [[nodiscard]] rpl::producer changeScreenVolumeRequests() const; [[nodiscard]] auto kickParticipantRequests() const -> rpl::producer>; @@ -188,6 +190,8 @@ class Members::Controller final rpl::event_stream _toggleMuteRequests; rpl::event_stream _changeVolumeRequests; + rpl::event_stream _toggleScreenMuteRequests; + rpl::event_stream _changeScreenVolumeRequests; rpl::event_stream> _kickParticipantRequests; rpl::variable _fullCount = 1; @@ -1047,6 +1051,16 @@ auto Members::Controller::changeVolumeRequests() const return _changeVolumeRequests.events(); } +auto Members::Controller::toggleScreenMuteRequests() const +-> rpl::producer { + return _toggleScreenMuteRequests.events(); +} + +auto Members::Controller::changeScreenVolumeRequests() const +-> rpl::producer { + return _changeScreenVolumeRequests.events(); +} + bool Members::Controller::rowIsMe(not_null participantPeer) { return isMe(participantPeer); } @@ -1581,11 +1595,40 @@ void Members::Controller::addMuteActionsToContextMenu( .locallyOnly = local, }); }); + const auto toggleScreenMute = crl::guard(this, [=](bool mute) { + _toggleScreenMuteRequests.fire(Group::MuteRequest{ + .peer = participantPeer, + .mute = mute, + .locallyOnly = true, + }); + }); + const auto changeScreenVolume = crl::guard(this, [=](int volume) { + _changeScreenVolumeRequests.fire(Group::VolumeRequest{ + .peer = participantPeer, + .volume = std::clamp(volume, 1, Group::kMaxVolume), + .locallyOnly = true, + }); + }); + const auto addSectionLabel = [=](const QString &text) { + const auto action = menu->addAction(text, [] {}); + action->setEnabled(false); + }; + const auto screenMuteActionText = [=](bool muted) { + return tr::lng_group_call_screen_share_audio(tr::now) + + u": "_q + + (muted + ? tr::lng_call_unmute_audio(tr::now) + : tr::lng_call_mute_audio(tr::now)); + }; const auto muteState = row->state(); const auto muted = (muteState == Row::State::Muted) || (muteState == Row::State::RaisedHand); const auto mutedByMe = row->mutedByMe(); + const auto addScreenVolumeItem = !_call->rtmp() + && !_call->videoStream() + && !isMe(participantPeer) + && _call->hasScreenShareAudio(participantPeer); auto mutesFromVolume = rpl::never() | rpl::type_erased; @@ -1643,16 +1686,70 @@ void Members::Controller::addMuteActionsToContextMenu( if (menu->actions().size() > 1) { // First - cover. menu->addSeparator(); } + if (addScreenVolumeItem) { + addSectionLabel(tr::lng_group_call_microphone(tr::now)); + } menu->addAction(std::move(volumeItem)); - if (!_call->rtmp() - && !_call->videoStream() - && !isMe(participantPeer)) { + if (!addScreenVolumeItem && !isMe(participantPeer)) { menu->addSeparator(); } }; + if (addScreenVolumeItem) { + auto otherParticipantScreenStateValue = rpl::merge( + _call->otherParticipantScreenStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.peer == participantPeer; + }), + _call->otherParticipantStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.peer == participantPeer; + }) | rpl::map([=](const Group::ParticipantState &) { + return _call->participantScreenState(participantPeer); + })); + + const auto state = _call->participantScreenState(participantPeer); + auto volumeItem = base::make_unique_q( + menu->menu(), + st::groupCallPopupVolumeMenu, + st::groupCallMenuVolumeSlider, + std::move(otherParticipantScreenStateValue), + state.volume.value_or(Group::kDefaultVolume), + Group::kMaxVolume, + state.mutedByMe, + st::groupCallMenuVolumePadding); + + volumeItem->toggleMuteRequests( + ) | rpl::on_next([=](bool muted) { + toggleScreenMute(muted); + }, volumeItem->lifetime()); + + volumeItem->toggleMuteLocallyRequests( + ) | rpl::on_next([=](bool muted) { + toggleScreenMute(muted); + }, volumeItem->lifetime()); + + volumeItem->changeVolumeRequests( + ) | rpl::on_next([=](int volume) { + changeScreenVolume(volume); + }, volumeItem->lifetime()); + + volumeItem->changeVolumeLocallyRequests( + ) | rpl::on_next([=](int volume) { + changeScreenVolume(volume); + }, volumeItem->lifetime()); + + if (menu->actions().size() > 1) { + menu->addSeparator(); + } + addSectionLabel(tr::lng_group_call_screen_share_audio(tr::now)); + + menu->addAction(std::move(volumeItem)); + menu->addSeparator(); + } + const auto muteAction = [&]() -> QAction* { if (muteState == Row::State::Invited || muteState == Row::State::Calling @@ -1696,6 +1793,29 @@ void Members::Controller::addMuteActionsToContextMenu( muteAction->setText(muteUnmuteString(muted, mutedByMe)); }, menu->lifetime()); } + if (addScreenVolumeItem) { + const auto initial = _call->participantScreenState(participantPeer); + const auto screenMuteAction = menu->addAction( + screenMuteActionText(initial.mutedByMe), + [=] { + const auto state = _call->participantScreenState(participantPeer); + toggleScreenMute(!state.mutedByMe); + }); + rpl::merge( + _call->otherParticipantScreenStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.peer == participantPeer; + }), + _call->otherParticipantStateValue( + ) | rpl::filter([=](const Group::ParticipantState &data) { + return data.peer == participantPeer; + }) | rpl::map([=](const Group::ParticipantState &) { + return _call->participantScreenState(participantPeer); + }) + ) | rpl::on_next([=](const Group::ParticipantState &state) { + screenMuteAction->setText(screenMuteActionText(state.mutedByMe)); + }, menu->lifetime()); + } } std::unique_ptr Members::Controller::createRowForMe() { @@ -1782,6 +1902,16 @@ auto Members::changeVolumeRequests() const return _listController->changeVolumeRequests(); } +auto Members::toggleScreenMuteRequests() const +-> rpl::producer { + return _listController->toggleScreenMuteRequests(); +} + +auto Members::changeScreenVolumeRequests() const +-> rpl::producer { + return _listController->changeScreenVolumeRequests(); +} + auto Members::kickParticipantRequests() const -> rpl::producer> { return _listController->kickParticipantRequests(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.h b/Telegram/SourceFiles/calls/group/calls_group_members.h index 739cccc990300d..656c48672d1e48 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.h +++ b/Telegram/SourceFiles/calls/group/calls_group_members.h @@ -55,6 +55,10 @@ class Members final -> rpl::producer; [[nodiscard]] auto changeVolumeRequests() const -> rpl::producer; + [[nodiscard]] auto toggleScreenMuteRequests() const + -> rpl::producer; + [[nodiscard]] auto changeScreenVolumeRequests() const + -> rpl::producer; [[nodiscard]] auto kickParticipantRequests() const -> rpl::producer>; [[nodiscard]] rpl::producer<> addMembersRequests() const { diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index fc6112da01b684..24ef08b7b3d252 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -1051,6 +1051,16 @@ void Panel::setupMembers() { _call->changeVolume(request); }, _callLifetime); + _members->toggleScreenMuteRequests( + ) | rpl::on_next([=](MuteRequest request) { + _call->toggleScreenMute(request); + }, _callLifetime); + + _members->changeScreenVolumeRequests( + ) | rpl::on_next([=](VolumeRequest request) { + _call->changeScreenVolume(request); + }, _callLifetime); + _members->kickParticipantRequests( ) | rpl::on_next([=](not_null participantPeer) { kickParticipant(participantPeer);