mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Divide speaking status and background noise.
This commit is contained in:
parent
7f7e7b94d6
commit
15620b5c2d
7 changed files with 139 additions and 93 deletions
|
@ -245,7 +245,10 @@ Updates::Updates(not_null<Main::Session*> session)
|
||||||
for (const auto [userId, when] : *users) {
|
for (const auto [userId, when] : *users) {
|
||||||
call->applyActiveUpdate(
|
call->applyActiveUpdate(
|
||||||
userId,
|
userId,
|
||||||
when,
|
Data::LastSpokeTimes{
|
||||||
|
.anything = when,
|
||||||
|
.voice = when
|
||||||
|
},
|
||||||
peer->owner().userLoaded(userId));
|
peer->owner().userLoaded(userId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -928,7 +931,10 @@ void Updates::handleSendActionUpdate(
|
||||||
const auto call = peer->groupCall();
|
const auto call = peer->groupCall();
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
if (call) {
|
if (call) {
|
||||||
call->applyActiveUpdate(userId, now, user);
|
call->applyActiveUpdate(
|
||||||
|
userId,
|
||||||
|
Data::LastSpokeTimes{ .anything = now, .voice = now },
|
||||||
|
user);
|
||||||
} else {
|
} else {
|
||||||
const auto chat = peer->asChat();
|
const auto chat = peer->asChat();
|
||||||
const auto channel = peer->asChannel();
|
const auto channel = peer->asChannel();
|
||||||
|
|
|
@ -498,28 +498,30 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::createAndStartController() {
|
void GroupCall::createAndStartController() {
|
||||||
using AudioLevels = std::vector<std::pair<uint32_t, float>>;
|
|
||||||
|
|
||||||
const auto &settings = Core::App().settings();
|
const auto &settings = Core::App().settings();
|
||||||
|
|
||||||
const auto weak = base::make_weak(this);
|
const auto weak = base::make_weak(this);
|
||||||
const auto myLevel = std::make_shared<float>();
|
const auto myLevel = std::make_shared<tgcalls::GroupLevelValue>();
|
||||||
tgcalls::GroupInstanceDescriptor descriptor = {
|
tgcalls::GroupInstanceDescriptor descriptor = {
|
||||||
.config = tgcalls::GroupConfig{
|
.config = tgcalls::GroupConfig{
|
||||||
},
|
},
|
||||||
.networkStateUpdated = [=](bool connected) {
|
.networkStateUpdated = [=](bool connected) {
|
||||||
crl::on_main(weak, [=] { setInstanceConnected(connected); });
|
crl::on_main(weak, [=] { setInstanceConnected(connected); });
|
||||||
},
|
},
|
||||||
.audioLevelsUpdated = [=](const AudioLevels &data) {
|
.audioLevelsUpdated = [=](const tgcalls::GroupLevelsUpdate &data) {
|
||||||
if (!data.empty()) {
|
const auto &updates = data.updates;
|
||||||
crl::on_main(weak, [=] { audioLevelsUpdated(data); });
|
if (updates.empty()) {
|
||||||
}
|
return;
|
||||||
},
|
} else if (updates.size() == 1 && !updates.front().ssrc) {
|
||||||
.myAudioLevelUpdated = [=](float level) {
|
const auto &value = updates.front().value;
|
||||||
if (*myLevel != level) { // Don't send many 0 while we're muted.
|
// Don't send many 0 while we're muted.
|
||||||
*myLevel = level;
|
if (myLevel->level == value.level
|
||||||
crl::on_main(weak, [=] { myLevelUpdated(level); });
|
&& myLevel->voice == value.voice) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*myLevel = updates.front().value;
|
||||||
}
|
}
|
||||||
|
crl::on_main(weak, [=] { audioLevelsUpdated(data); });
|
||||||
},
|
},
|
||||||
.initialInputDeviceId = settings.callInputDeviceId().toStdString(),
|
.initialInputDeviceId = settings.callInputDeviceId().toStdString(),
|
||||||
.initialOutputDeviceId = settings.callOutputDeviceId().toStdString(),
|
.initialOutputDeviceId = settings.callOutputDeviceId().toStdString(),
|
||||||
|
@ -555,24 +557,28 @@ void GroupCall::updateInstanceMuteState() {
|
||||||
&& state != MuteState::PushToTalk);
|
&& state != MuteState::PushToTalk);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::handleLevelsUpdated(
|
void GroupCall::audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data) {
|
||||||
gsl::span<const std::pair<std::uint32_t, float>> data) {
|
Expects(!data.updates.empty());
|
||||||
Expects(!data.empty());
|
|
||||||
|
|
||||||
auto check = false;
|
auto check = false;
|
||||||
auto checkNow = false;
|
auto checkNow = false;
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
for (const auto &[ssrc, level] : data) {
|
for (const auto &[ssrcOrZero, value] : data.updates) {
|
||||||
|
const auto ssrc = ssrcOrZero ? ssrcOrZero : _mySsrc;
|
||||||
|
const auto level = value.level;
|
||||||
|
const auto voice = value.voice;
|
||||||
const auto self = (ssrc == _mySsrc);
|
const auto self = (ssrc == _mySsrc);
|
||||||
_levelUpdates.fire(LevelUpdate{
|
_levelUpdates.fire(LevelUpdate{
|
||||||
.ssrc = ssrc,
|
.ssrc = ssrc,
|
||||||
.value = level,
|
.value = level,
|
||||||
|
.voice = voice,
|
||||||
.self = self
|
.self = self
|
||||||
});
|
});
|
||||||
if (level <= kSpeakLevelThreshold) {
|
if (level <= kSpeakLevelThreshold) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (self
|
if (self
|
||||||
|
&& voice
|
||||||
&& (!_lastSendProgressUpdate
|
&& (!_lastSendProgressUpdate
|
||||||
|| _lastSendProgressUpdate + kUpdateSendActionEach < now)) {
|
|| _lastSendProgressUpdate + kUpdateSendActionEach < now)) {
|
||||||
_lastSendProgressUpdate = now;
|
_lastSendProgressUpdate = now;
|
||||||
|
@ -584,13 +590,21 @@ void GroupCall::handleLevelsUpdated(
|
||||||
check = true;
|
check = true;
|
||||||
const auto i = _lastSpoke.find(ssrc);
|
const auto i = _lastSpoke.find(ssrc);
|
||||||
if (i == _lastSpoke.end()) {
|
if (i == _lastSpoke.end()) {
|
||||||
_lastSpoke.emplace(ssrc, now);
|
_lastSpoke.emplace(ssrc, Data::LastSpokeTimes{
|
||||||
|
.anything = now,
|
||||||
|
.voice = voice ? now : 0,
|
||||||
|
});
|
||||||
checkNow = true;
|
checkNow = true;
|
||||||
} else {
|
} else {
|
||||||
if (i->second + kCheckLastSpokeInterval / 3 <= now) {
|
if ((i->second.anything + kCheckLastSpokeInterval / 3 <= now)
|
||||||
|
|| (voice
|
||||||
|
&& i->second.voice + kCheckLastSpokeInterval / 3 <= now)) {
|
||||||
checkNow = true;
|
checkNow = true;
|
||||||
}
|
}
|
||||||
i->second = now;
|
i->second.anything = now;
|
||||||
|
if (voice) {
|
||||||
|
i->second.voice = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (checkNow) {
|
if (checkNow) {
|
||||||
|
@ -600,16 +614,6 @@ void GroupCall::handleLevelsUpdated(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::myLevelUpdated(float level) {
|
|
||||||
const auto pair = std::pair<std::uint32_t, float>{ _mySsrc, level };
|
|
||||||
handleLevelsUpdated({ &pair, &pair + 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupCall::audioLevelsUpdated(
|
|
||||||
const std::vector<std::pair<std::uint32_t, float>> &data) {
|
|
||||||
handleLevelsUpdated(gsl::make_span(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupCall::checkLastSpoke() {
|
void GroupCall::checkLastSpoke() {
|
||||||
const auto real = _peer->groupCall();
|
const auto real = _peer->groupCall();
|
||||||
if (!real || real->id() != _id) {
|
if (!real || real->id() != _id) {
|
||||||
|
@ -621,7 +625,7 @@ void GroupCall::checkLastSpoke() {
|
||||||
auto list = base::take(_lastSpoke);
|
auto list = base::take(_lastSpoke);
|
||||||
for (auto i = list.begin(); i != list.end();) {
|
for (auto i = list.begin(); i != list.end();) {
|
||||||
const auto [ssrc, when] = *i;
|
const auto [ssrc, when] = *i;
|
||||||
if (when + kCheckLastSpokeInterval >= now) {
|
if (when.anything + kCheckLastSpokeInterval >= now) {
|
||||||
hasRecent = true;
|
hasRecent = true;
|
||||||
++i;
|
++i;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,6 +17,7 @@ class History;
|
||||||
|
|
||||||
namespace tgcalls {
|
namespace tgcalls {
|
||||||
class GroupInstanceImpl;
|
class GroupInstanceImpl;
|
||||||
|
struct GroupLevelsUpdate;
|
||||||
} // namespace tgcalls
|
} // namespace tgcalls
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
@ -24,6 +25,10 @@ class GlobalShortcutManager;
|
||||||
class GlobalShortcutValue;
|
class GlobalShortcutValue;
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct LastSpokeTimes;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Calls {
|
namespace Calls {
|
||||||
|
|
||||||
enum class MuteState {
|
enum class MuteState {
|
||||||
|
@ -42,6 +47,7 @@ enum class MuteState {
|
||||||
struct LevelUpdate {
|
struct LevelUpdate {
|
||||||
uint32 ssrc = 0;
|
uint32 ssrc = 0;
|
||||||
float value = 0.;
|
float value = 0.;
|
||||||
|
bool voice = false;
|
||||||
bool self = false;
|
bool self = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,11 +152,7 @@ private:
|
||||||
void applySelfInCallLocally();
|
void applySelfInCallLocally();
|
||||||
void rejoin();
|
void rejoin();
|
||||||
|
|
||||||
void myLevelUpdated(float level);
|
void audioLevelsUpdated(const tgcalls::GroupLevelsUpdate &data);
|
||||||
void audioLevelsUpdated(
|
|
||||||
const std::vector<std::pair<std::uint32_t, float>> &data);
|
|
||||||
void handleLevelsUpdated(
|
|
||||||
gsl::span<const std::pair<std::uint32_t, float>> data);
|
|
||||||
void setInstanceConnected(bool connected);
|
void setInstanceConnected(bool connected);
|
||||||
void checkLastSpoke();
|
void checkLastSpoke();
|
||||||
void pushToTalkCancel();
|
void pushToTalkCancel();
|
||||||
|
@ -178,7 +180,7 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;
|
std::unique_ptr<tgcalls::GroupInstanceImpl> _instance;
|
||||||
rpl::event_stream<LevelUpdate> _levelUpdates;
|
rpl::event_stream<LevelUpdate> _levelUpdates;
|
||||||
base::flat_map<uint32, crl::time> _lastSpoke;
|
base::flat_map<uint32, Data::LastSpokeTimes> _lastSpoke;
|
||||||
base::Timer _lastSpokeCheckTimer;
|
base::Timer _lastSpokeCheckTimer;
|
||||||
base::Timer _checkJoinedTimer;
|
base::Timer _checkJoinedTimer;
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,9 @@ public:
|
||||||
[[nodiscard]] uint32 ssrc() const {
|
[[nodiscard]] uint32 ssrc() const {
|
||||||
return _ssrc;
|
return _ssrc;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] bool sounding() const {
|
||||||
|
return _sounding;
|
||||||
|
}
|
||||||
[[nodiscard]] bool speaking() const {
|
[[nodiscard]] bool speaking() const {
|
||||||
return _speaking;
|
return _speaking;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +150,7 @@ private:
|
||||||
|
|
||||||
Ui::Paint::Blobs blobs;
|
Ui::Paint::Blobs blobs;
|
||||||
crl::time lastTime = 0;
|
crl::time lastTime = 0;
|
||||||
crl::time lastSpeakingUpdateTime = 0;
|
crl::time lastSoundingUpdateTime = 0;
|
||||||
float64 enter = 0.;
|
float64 enter = 0.;
|
||||||
|
|
||||||
QImage userpicCache;
|
QImage userpicCache;
|
||||||
|
@ -156,6 +159,7 @@ private:
|
||||||
rpl::lifetime lifetime;
|
rpl::lifetime lifetime;
|
||||||
};
|
};
|
||||||
void refreshStatus() override;
|
void refreshStatus() override;
|
||||||
|
void setSounding(bool sounding);
|
||||||
void setSpeaking(bool speaking);
|
void setSpeaking(bool speaking);
|
||||||
void setState(State state);
|
void setState(State state);
|
||||||
void setSsrc(uint32 ssrc);
|
void setSsrc(uint32 ssrc);
|
||||||
|
@ -172,6 +176,7 @@ private:
|
||||||
Ui::Animations::Simple _mutedAnimation; // For gray/red icon.
|
Ui::Animations::Simple _mutedAnimation; // For gray/red icon.
|
||||||
Ui::Animations::Simple _activeAnimation; // For icon cross animation.
|
Ui::Animations::Simple _activeAnimation; // For icon cross animation.
|
||||||
uint32 _ssrc = 0;
|
uint32 _ssrc = 0;
|
||||||
|
bool _sounding = false;
|
||||||
bool _speaking = false;
|
bool _speaking = false;
|
||||||
bool _skipLevelUpdate = false;
|
bool _skipLevelUpdate = false;
|
||||||
|
|
||||||
|
@ -252,10 +257,10 @@ private:
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||||
base::flat_set<not_null<PeerData*>> _menuCheckRowsAfterHidden;
|
base::flat_set<not_null<PeerData*>> _menuCheckRowsAfterHidden;
|
||||||
|
|
||||||
base::flat_map<uint32, not_null<Row*>> _speakingRowBySsrc;
|
base::flat_map<uint32, not_null<Row*>> _soundingRowBySsrc;
|
||||||
Ui::Animations::Basic _speakingAnimation;
|
Ui::Animations::Basic _soundingAnimation;
|
||||||
|
|
||||||
crl::time _speakingAnimationHideLastTime = 0;
|
crl::time _soundingAnimationHideLastTime = 0;
|
||||||
bool _skipRowLevelUpdate = false;
|
bool _skipRowLevelUpdate = false;
|
||||||
|
|
||||||
Ui::CrossLineAnimation _inactiveCrossLine;
|
Ui::CrossLineAnimation _inactiveCrossLine;
|
||||||
|
@ -284,16 +289,20 @@ void Row::updateState(const Data::GroupCall::Participant *participant) {
|
||||||
setCustomStatus(QString());
|
setCustomStatus(QString());
|
||||||
}
|
}
|
||||||
setState(State::Inactive);
|
setState(State::Inactive);
|
||||||
|
setSounding(false);
|
||||||
setSpeaking(false);
|
setSpeaking(false);
|
||||||
} else if (!participant->muted
|
} else if (!participant->muted
|
||||||
|| (participant->speaking && participant->ssrc != 0)) {
|
|| (participant->sounding && participant->ssrc != 0)) {
|
||||||
setState(State::Active);
|
setState(State::Active);
|
||||||
|
setSounding(participant->sounding && participant->ssrc != 0);
|
||||||
setSpeaking(participant->speaking && participant->ssrc != 0);
|
setSpeaking(participant->speaking && participant->ssrc != 0);
|
||||||
} else if (participant->canSelfUnmute) {
|
} else if (participant->canSelfUnmute) {
|
||||||
setState(State::Inactive);
|
setState(State::Inactive);
|
||||||
|
setSounding(false);
|
||||||
setSpeaking(false);
|
setSpeaking(false);
|
||||||
} else {
|
} else {
|
||||||
setState(State::Muted);
|
setState(State::Muted);
|
||||||
|
setSounding(false);
|
||||||
setSpeaking(false);
|
setSpeaking(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +317,14 @@ void Row::setSpeaking(bool speaking) {
|
||||||
_speaking ? 0. : 1.,
|
_speaking ? 0. : 1.,
|
||||||
_speaking ? 1. : 0.,
|
_speaking ? 1. : 0.,
|
||||||
st::widgetFadeDuration);
|
st::widgetFadeDuration);
|
||||||
if (!_speaking) {
|
}
|
||||||
|
|
||||||
|
void Row::setSounding(bool sounding) {
|
||||||
|
if (_sounding == sounding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_sounding = sounding;
|
||||||
|
if (!_sounding) {
|
||||||
_blobsAnimation = nullptr;
|
_blobsAnimation = nullptr;
|
||||||
} else if (!_blobsAnimation) {
|
} else if (!_blobsAnimation) {
|
||||||
_blobsAnimation = std::make_unique<BlobsAnimation>(
|
_blobsAnimation = std::make_unique<BlobsAnimation>(
|
||||||
|
@ -358,7 +374,7 @@ void Row::updateLevel(float level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level >= GroupCall::kSpeakLevelThreshold) {
|
if (level >= GroupCall::kSpeakLevelThreshold) {
|
||||||
_blobsAnimation->lastSpeakingUpdateTime = crl::now();
|
_blobsAnimation->lastSoundingUpdateTime = crl::now();
|
||||||
}
|
}
|
||||||
_blobsAnimation->blobs.setLevel(level);
|
_blobsAnimation->blobs.setLevel(level);
|
||||||
}
|
}
|
||||||
|
@ -366,14 +382,14 @@ void Row::updateLevel(float level) {
|
||||||
void Row::updateBlobAnimation(crl::time now) {
|
void Row::updateBlobAnimation(crl::time now) {
|
||||||
Expects(_blobsAnimation != nullptr);
|
Expects(_blobsAnimation != nullptr);
|
||||||
|
|
||||||
const auto speakingFinishesAt = _blobsAnimation->lastSpeakingUpdateTime
|
const auto soundingFinishesAt = _blobsAnimation->lastSoundingUpdateTime
|
||||||
+ Data::GroupCall::kSpeakStatusKeptFor;
|
+ Data::GroupCall::kSoundStatusKeptFor;
|
||||||
const auto speakingStartsFinishing = speakingFinishesAt
|
const auto soundingStartsFinishing = soundingFinishesAt
|
||||||
- kBlobsEnterDuration;
|
- kBlobsEnterDuration;
|
||||||
const auto speakingFinishes = (speakingStartsFinishing < now);
|
const auto soundingFinishes = (soundingStartsFinishing < now);
|
||||||
if (speakingFinishes) {
|
if (soundingFinishes) {
|
||||||
_blobsAnimation->enter = std::clamp(
|
_blobsAnimation->enter = std::clamp(
|
||||||
(speakingFinishesAt - now) / float64(kBlobsEnterDuration),
|
(soundingFinishesAt - now) / float64(kBlobsEnterDuration),
|
||||||
0.,
|
0.,
|
||||||
1.);
|
1.);
|
||||||
} else if (_blobsAnimation->enter < 1.) {
|
} else if (_blobsAnimation->enter < 1.) {
|
||||||
|
@ -418,9 +434,15 @@ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
|
||||||
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||||
if (_blobsAnimation) {
|
if (_blobsAnimation) {
|
||||||
const auto shift = QPointF(x + size / 2., y + size / 2.);
|
const auto shift = QPointF(x + size / 2., y + size / 2.);
|
||||||
|
const auto speaking = _speakingAnimation.value(
|
||||||
|
_speaking ? 1. : 0.);
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
p.translate(shift);
|
p.translate(shift);
|
||||||
_blobsAnimation->blobs.paint(p, st::groupCallMemberActiveStatus);
|
const auto brush = anim::brush(
|
||||||
|
st::groupCallMemberInactiveStatus,
|
||||||
|
st::groupCallMemberActiveStatus,
|
||||||
|
speaking);
|
||||||
|
_blobsAnimation->blobs.paint(p, brush);
|
||||||
p.translate(-shift);
|
p.translate(-shift);
|
||||||
p.setOpacity(1.);
|
p.setOpacity(1.);
|
||||||
|
|
||||||
|
@ -537,28 +559,28 @@ MembersController::MembersController(
|
||||||
) | rpl::start_with_next([=](bool animDisabled, bool deactivated) {
|
) | rpl::start_with_next([=](bool animDisabled, bool deactivated) {
|
||||||
const auto hide = !(!animDisabled && !deactivated);
|
const auto hide = !(!animDisabled && !deactivated);
|
||||||
|
|
||||||
if (!(hide && _speakingAnimationHideLastTime)) {
|
if (!(hide && _soundingAnimationHideLastTime)) {
|
||||||
_speakingAnimationHideLastTime = hide ? crl::now() : 0;
|
_soundingAnimationHideLastTime = hide ? crl::now() : 0;
|
||||||
}
|
}
|
||||||
for (const auto [_, row] : _speakingRowBySsrc) {
|
for (const auto [_, row] : _soundingRowBySsrc) {
|
||||||
if (hide) {
|
if (hide) {
|
||||||
updateRowLevel(row, 0.);
|
updateRowLevel(row, 0.);
|
||||||
}
|
}
|
||||||
row->setSkipLevelUpdate(hide);
|
row->setSkipLevelUpdate(hide);
|
||||||
}
|
}
|
||||||
if (!hide && !_speakingAnimation.animating()) {
|
if (!hide && !_soundingAnimation.animating()) {
|
||||||
_speakingAnimation.start();
|
_soundingAnimation.start();
|
||||||
}
|
}
|
||||||
_skipRowLevelUpdate = hide;
|
_skipRowLevelUpdate = hide;
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
_speakingAnimation.init([=](crl::time now) {
|
_soundingAnimation.init([=](crl::time now) {
|
||||||
if (const auto &last = _speakingAnimationHideLastTime; (last > 0)
|
if (const auto &last = _soundingAnimationHideLastTime; (last > 0)
|
||||||
&& (now - last >= kBlobsEnterDuration)) {
|
&& (now - last >= kBlobsEnterDuration)) {
|
||||||
_speakingAnimation.stop();
|
_soundingAnimation.stop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const auto [ssrc, row] : _speakingRowBySsrc) {
|
for (const auto [ssrc, row] : _soundingRowBySsrc) {
|
||||||
row->updateBlobAnimation(now);
|
row->updateBlobAnimation(now);
|
||||||
delegate()->peerListUpdateRow(row);
|
delegate()->peerListUpdateRow(row);
|
||||||
}
|
}
|
||||||
|
@ -600,8 +622,8 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
||||||
|
|
||||||
call->levelUpdates(
|
call->levelUpdates(
|
||||||
) | rpl::start_with_next([=](const LevelUpdate &update) {
|
) | rpl::start_with_next([=](const LevelUpdate &update) {
|
||||||
const auto i = _speakingRowBySsrc.find(update.ssrc);
|
const auto i = _soundingRowBySsrc.find(update.ssrc);
|
||||||
if (i != end(_speakingRowBySsrc)) {
|
if (i != end(_soundingRowBySsrc)) {
|
||||||
updateRowLevel(i->second, update.value);
|
updateRowLevel(i->second, update.value);
|
||||||
}
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
@ -699,41 +721,41 @@ void MembersController::checkSpeakingRowPosition(not_null<Row*> row) {
|
||||||
void MembersController::updateRow(
|
void MembersController::updateRow(
|
||||||
not_null<Row*> row,
|
not_null<Row*> row,
|
||||||
const Data::GroupCall::Participant *participant) {
|
const Data::GroupCall::Participant *participant) {
|
||||||
const auto wasSpeaking = row->speaking();
|
const auto wasSounding = row->sounding();
|
||||||
const auto wasSsrc = row->ssrc();
|
const auto wasSsrc = row->ssrc();
|
||||||
row->setSkipLevelUpdate(_skipRowLevelUpdate);
|
row->setSkipLevelUpdate(_skipRowLevelUpdate);
|
||||||
row->updateState(participant);
|
row->updateState(participant);
|
||||||
const auto nowSpeaking = row->speaking();
|
const auto nowSounding = row->sounding();
|
||||||
const auto nowSsrc = row->ssrc();
|
const auto nowSsrc = row->ssrc();
|
||||||
|
|
||||||
const auto wasNoSpeaking = _speakingRowBySsrc.empty();
|
const auto wasNoSounding = _soundingRowBySsrc.empty();
|
||||||
if (wasSsrc == nowSsrc) {
|
if (wasSsrc == nowSsrc) {
|
||||||
if (nowSpeaking != wasSpeaking) {
|
if (nowSounding != wasSounding) {
|
||||||
if (nowSpeaking) {
|
if (nowSounding) {
|
||||||
_speakingRowBySsrc.emplace(nowSsrc, row);
|
_soundingRowBySsrc.emplace(nowSsrc, row);
|
||||||
} else {
|
} else {
|
||||||
_speakingRowBySsrc.remove(nowSsrc);
|
_soundingRowBySsrc.remove(nowSsrc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_speakingRowBySsrc.remove(wasSsrc);
|
_soundingRowBySsrc.remove(wasSsrc);
|
||||||
if (nowSpeaking) {
|
if (nowSounding) {
|
||||||
Assert(nowSsrc != 0);
|
Assert(nowSsrc != 0);
|
||||||
_speakingRowBySsrc.emplace(nowSsrc, row);
|
_soundingRowBySsrc.emplace(nowSsrc, row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto nowNoSpeaking = _speakingRowBySsrc.empty();
|
const auto nowNoSounding = _soundingRowBySsrc.empty();
|
||||||
if (wasNoSpeaking && !nowNoSpeaking) {
|
if (wasNoSounding && !nowNoSounding) {
|
||||||
_speakingAnimation.start();
|
_soundingAnimation.start();
|
||||||
} else if (nowNoSpeaking && !wasNoSpeaking) {
|
} else if (nowNoSounding && !wasNoSounding) {
|
||||||
_speakingAnimation.stop();
|
_soundingAnimation.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate()->peerListUpdateRow(row);
|
delegate()->peerListUpdateRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MembersController::removeRow(not_null<Row*> row) {
|
void MembersController::removeRow(not_null<Row*> row) {
|
||||||
_speakingRowBySsrc.remove(row->ssrc());
|
_soundingRowBySsrc.remove(row->ssrc());
|
||||||
delegate()->peerListRemoveRow(row);
|
delegate()->peerListRemoveRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -342,7 +342,10 @@ void GroupCall::applyParticipantsMutes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) {
|
void GroupCall::applyLastSpoke(
|
||||||
|
uint32 ssrc,
|
||||||
|
LastSpokeTimes when,
|
||||||
|
crl::time now) {
|
||||||
const auto i = _userBySsrc.find(ssrc);
|
const auto i = _userBySsrc.find(ssrc);
|
||||||
if (i == end(_userBySsrc)) {
|
if (i == end(_userBySsrc)) {
|
||||||
_unknownSpokenSsrcs[ssrc] = when;
|
_unknownSpokenSsrcs[ssrc] = when;
|
||||||
|
@ -353,10 +356,13 @@ void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) {
|
||||||
Assert(j != end(_participants));
|
Assert(j != end(_participants));
|
||||||
|
|
||||||
_speakingByActiveFinishes.remove(j->user);
|
_speakingByActiveFinishes.remove(j->user);
|
||||||
const auto speaking = (when + kSpeakStatusKeptFor >= now)
|
const auto sounding = (when.anything + kSoundStatusKeptFor >= now)
|
||||||
&& j->canSelfUnmute;
|
&& j->canSelfUnmute;
|
||||||
if (j->speaking != speaking) {
|
const auto speaking = sounding
|
||||||
|
&& (when.voice + kSoundStatusKeptFor >= now);
|
||||||
|
if (j->sounding != sounding || j->speaking != speaking) {
|
||||||
const auto was = *j;
|
const auto was = *j;
|
||||||
|
j->sounding = sounding;
|
||||||
j->speaking = speaking;
|
j->speaking = speaking;
|
||||||
_participantUpdates.fire({
|
_participantUpdates.fire({
|
||||||
.was = was,
|
.was = was,
|
||||||
|
@ -367,7 +373,7 @@ void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) {
|
||||||
|
|
||||||
void GroupCall::applyActiveUpdate(
|
void GroupCall::applyActiveUpdate(
|
||||||
UserId userId,
|
UserId userId,
|
||||||
crl::time when,
|
LastSpokeTimes when,
|
||||||
UserData *userLoaded) {
|
UserData *userLoaded) {
|
||||||
if (inCall()) {
|
if (inCall()) {
|
||||||
return;
|
return;
|
||||||
|
@ -387,9 +393,9 @@ void GroupCall::applyActiveUpdate(
|
||||||
}
|
}
|
||||||
const auto was = std::make_optional(*i);
|
const auto was = std::make_optional(*i);
|
||||||
const auto now = crl::now();
|
const auto now = crl::now();
|
||||||
const auto elapsed = TimeId((now - when) / crl::time(1000));
|
const auto elapsed = TimeId((now - when.anything) / crl::time(1000));
|
||||||
const auto lastActive = base::unixtime::now() - elapsed;
|
const auto lastActive = base::unixtime::now() - elapsed;
|
||||||
const auto finishes = when + kSpeakingAfterActive;
|
const auto finishes = when.anything + kSpeakingAfterActive;
|
||||||
if (lastActive <= i->lastActive || finishes <= now) {
|
if (lastActive <= i->lastActive || finishes <= now) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -450,7 +456,7 @@ void GroupCall::requestUnknownParticipants() {
|
||||||
if (_unknownSpokenSsrcs.size() < kRequestPerPage) {
|
if (_unknownSpokenSsrcs.size() < kRequestPerPage) {
|
||||||
return base::take(_unknownSpokenSsrcs);
|
return base::take(_unknownSpokenSsrcs);
|
||||||
}
|
}
|
||||||
auto result = base::flat_map<uint32, crl::time>();
|
auto result = base::flat_map<uint32, LastSpokeTimes>();
|
||||||
result.reserve(kRequestPerPage);
|
result.reserve(kRequestPerPage);
|
||||||
while (result.size() < kRequestPerPage) {
|
while (result.size() < kRequestPerPage) {
|
||||||
const auto [ssrc, when] = _unknownSpokenSsrcs.back();
|
const auto [ssrc, when] = _unknownSpokenSsrcs.back();
|
||||||
|
@ -463,7 +469,7 @@ void GroupCall::requestUnknownParticipants() {
|
||||||
if (_unknownSpokenUids.size() + ssrcs.size() < kRequestPerPage) {
|
if (_unknownSpokenUids.size() + ssrcs.size() < kRequestPerPage) {
|
||||||
return base::take(_unknownSpokenUids);
|
return base::take(_unknownSpokenUids);
|
||||||
}
|
}
|
||||||
auto result = base::flat_map<UserId, crl::time>();
|
auto result = base::flat_map<UserId, LastSpokeTimes>();
|
||||||
const auto available = (kRequestPerPage - int(ssrcs.size()));
|
const auto available = (kRequestPerPage - int(ssrcs.size()));
|
||||||
if (available > 0) {
|
if (available > 0) {
|
||||||
result.reserve(available);
|
result.reserve(available);
|
||||||
|
|
|
@ -16,6 +16,11 @@ class ApiWrap;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
|
struct LastSpokeTimes {
|
||||||
|
crl::time anything = 0;
|
||||||
|
crl::time voice = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class GroupCall final {
|
class GroupCall final {
|
||||||
public:
|
public:
|
||||||
GroupCall(not_null<PeerData*> peer, uint64 id, uint64 accessHash);
|
GroupCall(not_null<PeerData*> peer, uint64 id, uint64 accessHash);
|
||||||
|
@ -32,6 +37,7 @@ public:
|
||||||
TimeId date = 0;
|
TimeId date = 0;
|
||||||
TimeId lastActive = 0;
|
TimeId lastActive = 0;
|
||||||
uint32 ssrc = 0;
|
uint32 ssrc = 0;
|
||||||
|
bool sounding = false;
|
||||||
bool speaking = false;
|
bool speaking = false;
|
||||||
bool muted = false;
|
bool muted = false;
|
||||||
bool canSelfUnmute = false;
|
bool canSelfUnmute = false;
|
||||||
|
@ -41,7 +47,7 @@ public:
|
||||||
std::optional<Participant> now;
|
std::optional<Participant> now;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr auto kSpeakStatusKeptFor = crl::time(350);
|
static constexpr auto kSoundStatusKeptFor = crl::time(350);
|
||||||
|
|
||||||
[[nodiscard]] auto participants() const
|
[[nodiscard]] auto participants() const
|
||||||
-> const std::vector<Participant> &;
|
-> const std::vector<Participant> &;
|
||||||
|
@ -56,10 +62,10 @@ public:
|
||||||
void applyUpdate(const MTPDupdateGroupCallParticipants &update);
|
void applyUpdate(const MTPDupdateGroupCallParticipants &update);
|
||||||
void applyUpdateChecked(
|
void applyUpdateChecked(
|
||||||
const MTPDupdateGroupCallParticipants &update);
|
const MTPDupdateGroupCallParticipants &update);
|
||||||
void applyLastSpoke(uint32 ssrc, crl::time when, crl::time now);
|
void applyLastSpoke(uint32 ssrc, LastSpokeTimes when, crl::time now);
|
||||||
void applyActiveUpdate(
|
void applyActiveUpdate(
|
||||||
UserId userId,
|
UserId userId,
|
||||||
crl::time when,
|
LastSpokeTimes when,
|
||||||
UserData *userLoaded);
|
UserData *userLoaded);
|
||||||
|
|
||||||
[[nodiscard]] int fullCount() const;
|
[[nodiscard]] int fullCount() const;
|
||||||
|
@ -106,8 +112,8 @@ private:
|
||||||
QString _nextOffset;
|
QString _nextOffset;
|
||||||
rpl::variable<int> _fullCount = 0;
|
rpl::variable<int> _fullCount = 0;
|
||||||
|
|
||||||
base::flat_map<uint32, crl::time> _unknownSpokenSsrcs;
|
base::flat_map<uint32, LastSpokeTimes> _unknownSpokenSsrcs;
|
||||||
base::flat_map<UserId, crl::time> _unknownSpokenUids;
|
base::flat_map<UserId, LastSpokeTimes> _unknownSpokenUids;
|
||||||
mtpRequestId _unknownUsersRequestId = 0;
|
mtpRequestId _unknownUsersRequestId = 0;
|
||||||
|
|
||||||
rpl::event_stream<ParticipantUpdate> _participantUpdates;
|
rpl::event_stream<ParticipantUpdate> _participantUpdates;
|
||||||
|
|
2
Telegram/ThirdParty/tgcalls
vendored
2
Telegram/ThirdParty/tgcalls
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit e884535f1854aa57616b162a4901bd60c8287575
|
Subproject commit b892eb58bc941a4a1a67303439df8ffd379d6051
|
Loading…
Add table
Reference in a new issue