Mute by me / change participant volume.

This commit is contained in:
John Preston 2020-12-29 18:54:17 +04:00
parent b396244606
commit f63f0a7668
7 changed files with 188 additions and 26 deletions

View File

@ -238,11 +238,25 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
using Update = Data::GroupCall::ParticipantUpdate;
_peer->groupCall()->participantUpdated(
) | rpl::filter([=](const Update &update) {
return (_instance != nullptr) && !update.now;
return (_instance != nullptr);
}) | rpl::start_with_next([=](const Update &update) {
Expects(update.was.has_value());
_instance->removeSsrcs({ update.was->ssrc });
if (!update.now) {
_instance->removeSsrcs({ update.was->ssrc });
} else {
const auto &now = *update.now;
const auto &was = update.was;
const auto volumeChanged = was
? (was->volume != now.volume || was->mutedByMe != now.mutedByMe)
: (now.volume != Data::GroupCall::kDefaultVolume || now.mutedByMe);
if (volumeChanged) {
_instance->setVolume(
now.ssrc,
(now.mutedByMe
? 0.
: (now.volume
/ float64(Data::GroupCall::kDefaultVolume))));
}
}
}, _lifetime);
SubscribeToMigration(_peer, _lifetime, [=](not_null<ChannelData*> group) {
@ -610,6 +624,7 @@ void GroupCall::createAndStartController() {
std::move(descriptor));
updateInstanceMuteState();
updateInstanceVolumes();
//raw->setAudioOutputDuckingEnabled(settings.callAudioDuckingEnabled());
}
@ -622,6 +637,27 @@ void GroupCall::updateInstanceMuteState() {
&& state != MuteState::PushToTalk);
}
void GroupCall::updateInstanceVolumes() {
const auto real = _peer->groupCall();
if (!real || real->id() != _id) {
return;
}
const auto &participants = real->participants();
for (const auto &participant : participants) {
const auto setVolume = participant.mutedByMe
|| (participant.volume != Data::GroupCall::kDefaultVolume);
if (setVolume && participant.ssrc) {
_instance->setVolume(
participant.ssrc,
(participant.mutedByMe
? 0.
: (participant.volume
/ float64(Data::GroupCall::kDefaultVolume))));
}
}
}
void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) {
Expects(!data.updates.empty());
@ -792,17 +828,46 @@ void GroupCall::setCurrentAudioDevice(bool input, const QString &deviceId) {
}
}
[[nodiscard]] const Data::GroupCall::Participant *LookupParticipant(
not_null<PeerData*> chat,
uint64 id,
not_null<UserData*> user) {
const auto call = chat->groupCall();
if (!id || !call || call->id() != id) {
return nullptr;
}
const auto &participants = call->participants();
const auto i = ranges::find(
participants,
user,
&Data::GroupCall::Participant::user);
return (i != end(participants)) ? &*i : nullptr;
}
void GroupCall::toggleMute(not_null<UserData*> user, bool mute) {
if (!_id) {
editParticipant(user, mute, std::nullopt);
}
void GroupCall::changeVolume(not_null<UserData*> user, int volume) {
editParticipant(user, false, volume);
}
void GroupCall::editParticipant(
not_null<UserData*> user,
bool mute,
std::optional<int> volume) {
const auto participant = LookupParticipant(_peer, _id, user);
if (!participant) {
return;
}
using Flag = MTPphone_EditGroupCallMember::Flag;
const auto flags = (mute ? Flag::f_muted : Flag(0))
| (volume.has_value() ? Flag::f_volume : Flag(0));
_api.request(MTPphone_EditGroupCallMember(
MTP_flags(mute
? MTPphone_EditGroupCallMember::Flag::f_muted
: MTPphone_EditGroupCallMember::Flag(0)),
MTP_flags(flags),
inputCall(),
user->inputUser,
MTP_int(100000) // volume
MTP_int(std::clamp(volume.value_or(0), 1, 20000))
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
}).fail([=](const RPCError &error) {

View File

@ -132,6 +132,7 @@ public:
void setAudioDuckingEnabled(bool enabled);
void toggleMute(not_null<UserData*> user, bool mute);
void changeVolume(not_null<UserData*> user, int volume);
std::variant<int, not_null<UserData*>> inviteUsers(
const std::vector<not_null<UserData*>> &users);
@ -163,6 +164,7 @@ private:
void maybeSendMutedUpdate(MuteState previous);
void sendMutedUpdate();
void updateInstanceMuteState();
void updateInstanceVolumes();
void applySelfInCallLocally();
void rejoin();
@ -178,6 +180,11 @@ private:
void stopConnectingSound();
void playConnectingSoundOnce();
void editParticipant(
not_null<UserData*> user,
bool mute,
std::optional<int> volume);
[[nodiscard]] MTPInputGroupCall inputCall() const;
const not_null<Delegate*> _delegate;

View File

@ -76,7 +76,8 @@ public:
QRect rect,
float64 speaking,
float64 active,
float64 muted) = 0;
float64 muted,
bool mutedByMe) = 0;
};
class Row final : public PeerListRow {
@ -87,6 +88,7 @@ public:
Active,
Inactive,
Muted,
MutedByMe,
Invited,
};
@ -106,6 +108,9 @@ public:
[[nodiscard]] bool speaking() const {
return _speaking;
}
[[nodiscard]] int volume() const {
return _volume;
}
void addActionRipple(QPoint point, Fn<void()> updateCallback) override;
void stopLastActionRipple() override;
@ -177,6 +182,7 @@ private:
void setSpeaking(bool speaking);
void setState(State state);
void setSsrc(uint32 ssrc);
void setVolume(int volume);
void ensureUserpicCache(
std::shared_ptr<Data::CloudImageView> &view,
@ -190,6 +196,7 @@ private:
Ui::Animations::Simple _mutedAnimation; // For gray/red icon.
Ui::Animations::Simple _activeAnimation; // For icon cross animation.
uint32 _ssrc = 0;
int _volume = Data::GroupCall::kDefaultVolume;
bool _sounding = false;
bool _speaking = false;
bool _skipLevelUpdate = false;
@ -207,6 +214,7 @@ public:
~MembersController();
using MuteRequest = GroupMembers::MuteRequest;
using VolumeRequest = GroupMembers::VolumeRequest;
Main::Session &session() const override;
void prepare() override;
@ -221,6 +229,7 @@ public:
return _fullCount.value();
}
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
[[nodiscard]] rpl::producer<VolumeRequest> changeVolumeRequests() const;
[[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>;
@ -231,7 +240,8 @@ public:
QRect rect,
float64 speaking,
float64 active,
float64 muted) override;
float64 muted,
bool mutedByMe) override;
private:
[[nodiscard]] std::unique_ptr<Row> createSelfRow();
@ -271,6 +281,7 @@ private:
bool _prepared = false;
rpl::event_stream<MuteRequest> _toggleMuteRequests;
rpl::event_stream<VolumeRequest> _changeVolumeRequests;
rpl::event_stream<not_null<UserData*>> _kickMemberRequests;
rpl::variable<int> _fullCount = 1;
rpl::variable<int> _fullCountMin = 0;
@ -305,17 +316,20 @@ void Row::setSkipLevelUpdate(bool value) {
void Row::updateState(const Data::GroupCall::Participant *participant) {
setSsrc(participant ? participant->ssrc : 0);
setVolume(participant
? participant->volume
: Data::GroupCall::kDefaultVolume);
if (!participant) {
setState(State::Invited);
setSounding(false);
setSpeaking(false);
} else if (!participant->muted
|| (participant->sounding && participant->ssrc != 0)) {
setState(State::Active);
setState(participant->mutedByMe ? State::MutedByMe : State::Active);
setSounding(participant->sounding && participant->ssrc != 0);
setSpeaking(participant->speaking && participant->ssrc != 0);
} else if (participant->canSelfUnmute) {
setState(State::Inactive);
setState(participant->mutedByMe ? State::MutedByMe : State::Inactive);
setSounding(false);
setSpeaking(false);
} else {
@ -384,6 +398,10 @@ void Row::setSsrc(uint32 ssrc) {
_ssrc = ssrc;
}
void Row::setVolume(int volume) {
_volume = volume;
}
void Row::updateLevel(float level) {
Expects(_blobsAnimation != nullptr);
@ -451,13 +469,16 @@ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
auto userpic = ensureUserpicView();
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
if (_blobsAnimation) {
const auto mutedByMe = (_state == State::MutedByMe);
const auto shift = QPointF(x + size / 2., y + size / 2.);
auto hq = PainterHighQualityEnabler(p);
p.translate(shift);
const auto brush = anim::brush(
st::groupCallMemberInactiveStatus,
st::groupCallMemberActiveStatus,
_speakingAnimation.value(_speaking ? 1. : 0.));
const auto brush = mutedByMe
? st::groupCallMemberMutedIcon->b
: anim::brush(
st::groupCallMemberInactiveStatus,
st::groupCallMemberActiveStatus,
_speakingAnimation.value(_speaking ? 1. : 0.));
_blobsAnimation->blobs.paint(p, brush);
p.translate(-shift);
p.setOpacity(1.);
@ -502,7 +523,7 @@ void Row::paintStatusText(
int availableWidth,
int outerWidth,
bool selected) {
if (_state != State::Invited) {
if (_state != State::Invited && _state != State::MutedByMe) {
PeerListRow::paintStatusText(
p,
st,
@ -514,12 +535,18 @@ void Row::paintStatusText(
return;
}
p.setFont(st::normalFont);
p.setPen(st::groupCallMemberNotJoinedStatus);
if (_state == State::MutedByMe) {
p.setPen(st::groupCallMemberMutedIcon);
} else {
p.setPen(st::groupCallMemberNotJoinedStatus);
}
p.drawTextLeft(
x,
y,
outerWidth,
(peer()->isSelf()
(_state == State::MutedByMe
? "muted by me"
: peer()->isSelf()
? tr::lng_status_connecting(tr::now)
: tr::lng_group_call_invited_status(tr::now)));
}
@ -561,7 +588,8 @@ void Row::paintAction(
(_state == State::Active) ? 1. : 0.);
const auto muted = _mutedAnimation.value(
(_state == State::Muted) ? 1. : 0.);
_delegate->rowPaintIcon(p, iconRect, speaking, active, muted);
const auto mutedByMe = (_state == State::MutedByMe);
_delegate->rowPaintIcon(p, iconRect, speaking, active, muted, mutedByMe);
}
void Row::refreshStatus() {
@ -984,6 +1012,11 @@ auto MembersController::toggleMuteRequests() const
return _toggleMuteRequests.events();
}
auto MembersController::changeVolumeRequests() const
-> rpl::producer<VolumeRequest> {
return _changeVolumeRequests.events();
}
bool MembersController::rowCanMuteMembers() {
return _peer->canManageGroupCall();
}
@ -997,11 +1030,12 @@ void MembersController::rowPaintIcon(
QRect rect,
float64 speaking,
float64 active,
float64 muted) {
float64 muted,
bool mutedByMe) {
const auto &greenIcon = st::groupCallMemberColoredCrossLine.icon;
const auto left = rect.x() + (rect.width() - greenIcon.width()) / 2;
const auto top = rect.y() + (rect.height() - greenIcon.height()) / 2;
if (speaking == 1.) {
if (speaking == 1. && !mutedByMe) {
// Just green icon, no cross, no coloring.
greenIcon.paintInCenter(p, rect);
return;
@ -1029,7 +1063,9 @@ void MembersController::rowPaintIcon(
}
const auto activeInactiveColor = anim::color(
st::groupCallMemberInactiveIcon,
st::groupCallMemberActiveIcon,
(mutedByMe
? st::groupCallMemberMutedIcon
: st::groupCallMemberActiveIcon),
speaking);
const auto iconColor = anim::color(
activeInactiveColor,
@ -1119,7 +1155,8 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
}
return false;
}();
const auto mute = admin
const auto amCallAdmin = _peer->canManageGroupCall();
const auto mute = (admin || !amCallAdmin)
? (muteState == Row::State::Active)
: (muteState != Row::State::Muted);
const auto toggleMute = crl::guard(this, [=] {
@ -1128,6 +1165,12 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
.mute = mute,
});
});
const auto changeVolume = crl::guard(this, [=](int volume) {
_changeVolumeRequests.fire(VolumeRequest{
.user = user,
.volume = std::clamp(volume, 1, 20000),
});
});
const auto session = &user->session();
const auto getCurrentWindow = [=]() -> Window::SessionController* {
@ -1179,7 +1222,7 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
});
if ((muteState != Row::State::Invited)
&& _peer->canManageGroupCall()
&& amCallAdmin
&& (!admin || mute)) {
result->addAction(
(mute
@ -1187,6 +1230,24 @@ base::unique_qptr<Ui::PopupMenu> MembersController::createRowContextMenu(
: tr::lng_group_call_context_unmute(tr::now)),
toggleMute);
}
if (real->ssrc() != 0) {
if (!amCallAdmin
&& ((muteState == Row::State::Active)
|| (real->state() == Row::State::MutedByMe))) {
result->addAction(
((muteState == Row::State::Active)
? "Mute for me"
: "Unmute for me"),
toggleMute);
}
const auto volume = real->volume();
result->addAction(QString("Increase volume (%1%)").arg(volume / 100.), [=] {
changeVolume(volume + 2000);
});
result->addAction(QString("Decrease volume (%1%)").arg(volume / 100.), [=] {
changeVolume(volume - 2000);
});
}
result->addAction(
tr::lng_context_view_profile(tr::now),
showProfile);
@ -1238,6 +1299,9 @@ std::unique_ptr<Row> MembersController::createInvitedRow(
} // namespace
const int GroupMembers::kDefaultVolume = Data::GroupCall::kDefaultVolume;
GroupMembers::GroupMembers(
not_null<QWidget*> parent,
not_null<GroupCall*> call)
@ -1258,6 +1322,12 @@ auto GroupMembers::toggleMuteRequests() const
_listController.get())->toggleMuteRequests();
}
auto GroupMembers::changeVolumeRequests() const
-> rpl::producer<GroupMembers::VolumeRequest> {
return static_cast<MembersController*>(
_listController.get())->changeVolumeRequests();
}
auto GroupMembers::kickMemberRequests() const
-> rpl::producer<not_null<UserData*>> {
return static_cast<MembersController*>(

View File

@ -30,15 +30,23 @@ public:
not_null<QWidget*> parent,
not_null<GroupCall*> call);
static const int kDefaultVolume;/* = Data::GroupCall::kDefaultVolume*/
struct MuteRequest {
not_null<UserData*> user;
bool mute = false;
};
struct VolumeRequest {
not_null<UserData*> user;
int volume = kDefaultVolume;
bool finalized = true;
};
[[nodiscard]] int desiredHeight() const;
[[nodiscard]] rpl::producer<int> desiredHeightValue() const override;
[[nodiscard]] rpl::producer<int> fullCountValue() const;
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
[[nodiscard]] rpl::producer<VolumeRequest> changeVolumeRequests() const;
[[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>;
[[nodiscard]] rpl::producer<> addMembersRequests() const {

View File

@ -511,6 +511,13 @@ void GroupPanel::initWithCall(GroupCall *call) {
}
}, _callLifetime);
_members->changeVolumeRequests(
) | rpl::start_with_next([=](GroupMembers::VolumeRequest request) {
if (_call) {
_call->changeVolume(request.user, request.volume);
}
}, _callLifetime);
_members->kickMemberRequests(
) | rpl::start_with_next([=](not_null<UserData*> user) {
kickMember(user);

View File

@ -280,8 +280,10 @@ void GroupCall::applyParticipantsSlice(
.date = data.vdate().v,
.lastActive = lastActive,
.ssrc = uint32(data.vsource().v),
.volume = data.vvolume().value_or(kDefaultVolume),
.speaking = canSelfUnmute && (was ? was->speaking : false),
.muted = data.is_muted(),
.mutedByMe = data.is_muted_by_you(),
.canSelfUnmute = canSelfUnmute,
};
if (i == end(_participants)) {

View File

@ -32,14 +32,17 @@ public:
void setPeer(not_null<PeerData*> peer);
static constexpr auto kDefaultVolume = 10000;
struct Participant {
not_null<UserData*> user;
TimeId date = 0;
TimeId lastActive = 0;
uint32 ssrc = 0;
int volume = 0;
bool sounding = false;
bool speaking = false;
bool muted = false;
bool mutedByMe = false;
bool canSelfUnmute = false;
};
struct ParticipantUpdate {