Add blob animations to group call participants.

This commit is contained in:
John Preston 2020-12-01 12:06:06 +03:00
parent 80597e190a
commit 498d6226e3
6 changed files with 216 additions and 171 deletions

View file

@ -660,3 +660,6 @@ groupCallSettingsAttentionButton: SettingsButton(groupCallSettingsButton) {
groupCallBoxLabel: FlatLabel(boxLabel) { groupCallBoxLabel: FlatLabel(boxLabel) {
textFg: groupCallMembersFg; textFg: groupCallMembersFg;
} }
groupCallRowBlobMinRadius: 26px;
groupCallRowBlobMaxRadius: 30px;

View file

@ -152,7 +152,7 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) {
}) | rpl::start_with_next([=](const Update &update) { }) | rpl::start_with_next([=](const Update &update) {
Expects(update.was.has_value()); Expects(update.was.has_value());
_instance->removeSsrcs({ update.was->source }); _instance->removeSsrcs({ update.was->ssrc });
}, _lifetime); }, _lifetime);
} }
@ -190,7 +190,7 @@ void GroupCall::rejoin() {
root.insert("fingerprints", fingerprints); root.insert("fingerprints", fingerprints);
root.insert("ssrc", double(payload.ssrc)); root.insert("ssrc", double(payload.ssrc));
LOG(("Call Info: Join payload received, joining with source: %1." LOG(("Call Info: Join payload received, joining with ssrc: %1."
).arg(ssrc)); ).arg(ssrc));
const auto json = QJsonDocument(root).toJson( const auto json = QJsonDocument(root).toJson(
@ -418,12 +418,12 @@ void GroupCall::handleUpdate(const MTPDupdateGroupCallParticipants &data) {
} }
if (data.is_left() && data.vsource().v == _mySsrc) { if (data.is_left() && data.vsource().v == _mySsrc) {
// I was removed from the call, rejoin. // I was removed from the call, rejoin.
LOG(("Call Info: Rejoin after got 'left' with my source.")); LOG(("Call Info: Rejoin after got 'left' with my ssrc."));
setState(State::Joining); setState(State::Joining);
rejoin(); rejoin();
} else if (!data.is_left() && data.vsource().v != _mySsrc) { } else if (!data.is_left() && data.vsource().v != _mySsrc) {
// I joined from another device, hangup. // I joined from another device, hangup.
LOG(("Call Info: Hangup after '!left' with source %1, my %2." LOG(("Call Info: Hangup after '!left' with ssrc %1, my %2."
).arg(data.vsource().v ).arg(data.vsource().v
).arg(_mySsrc)); ).arg(_mySsrc));
_mySsrc = 0; _mySsrc = 0;
@ -495,10 +495,10 @@ void GroupCall::handleLevelsUpdated(
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 &[source, level] : data) { for (const auto &[ssrc, level] : data) {
const auto self = (source == _mySsrc); const auto self = (ssrc == _mySsrc);
_levelUpdates.fire(LevelUpdate{ _levelUpdates.fire(LevelUpdate{
.source = source, .ssrc = ssrc,
.value = level, .value = level,
.self = self .self = self
}); });
@ -515,9 +515,9 @@ void GroupCall::handleLevelsUpdated(
} }
check = true; check = true;
const auto i = _lastSpoke.find(source); const auto i = _lastSpoke.find(ssrc);
if (i == _lastSpoke.end()) { if (i == _lastSpoke.end()) {
_lastSpoke.emplace(source, now); _lastSpoke.emplace(ssrc, now);
checkNow = true; checkNow = true;
} else { } else {
if (i->second + kCheckLastSpokeInterval / 3 <= now) { if (i->second + kCheckLastSpokeInterval / 3 <= now) {
@ -553,14 +553,14 @@ void GroupCall::checkLastSpoke() {
const auto now = crl::now(); const auto now = crl::now();
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 [source, when] = *i; const auto [ssrc, when] = *i;
if (when + kCheckLastSpokeInterval >= now) { if (when + kCheckLastSpokeInterval >= now) {
hasRecent = true; hasRecent = true;
++i; ++i;
} else { } else {
i = list.erase(i); i = list.erase(i);
} }
real->applyLastSpoke(source, when, now); real->applyLastSpoke(ssrc, when, now);
} }
_lastSpoke = std::move(list); _lastSpoke = std::move(list);

View file

@ -28,7 +28,7 @@ enum class MuteState {
}; };
struct LevelUpdate { struct LevelUpdate {
uint32 source = 0; uint32 ssrc = 0;
float value = 0.; float value = 0.;
bool self = false; bool self = false;
}; };

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_group_call.h" #include "data/data_group_call.h"
#include "data/data_peer_values.h" // Data::CanWriteValue. #include "data/data_peer_values.h" // Data::CanWriteValue.
#include "ui/paint/blobs.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
@ -32,12 +33,37 @@ namespace Calls {
namespace { namespace {
constexpr auto kLevelThreshold = 0.2; constexpr auto kLevelThreshold = 0.2;
constexpr auto kRowBlobRadiusFactor = (float)(50. / 57.);
constexpr auto kLevelDuration = 100. + 500. * 0.33;
constexpr auto kScaleSmall = 0.704 - 0.1;
constexpr auto kScaleSmallMin = 0.926;
constexpr auto kScaleSmallMax = (float)(kScaleSmallMin + kScaleSmall);
constexpr auto kMaxLevel = 1.;
struct UpdateLevelResult { auto RowBlobs() -> std::array<Ui::Paint::Blobs::BlobData, 2> {
bool levelChanged = false; return { {
bool stateChanged = false; {
crl::time nextUpdateTime = 0; .segmentsCount = 6,
}; .minScale = kScaleSmallMin / kScaleSmallMax,
.minRadius = st::groupCallRowBlobMinRadius
* kRowBlobRadiusFactor,
.maxRadius = st::groupCallRowBlobMaxRadius
* kRowBlobRadiusFactor,
.speedScale = 1.,
.alpha = (76. / 255.),
},
{
.segmentsCount = 8,
.minScale = kScaleSmallMin / kScaleSmallMax,
.minRadius = st::groupCallRowBlobMinRadius
* kRowBlobRadiusFactor,
.maxRadius = st::groupCallRowBlobMaxRadius
* kRowBlobRadiusFactor,
.speedScale = 1.,
.alpha = (76. / 255.),
},
} };
}
class Row final : public PeerListRow { class Row final : public PeerListRow {
public: public:
@ -50,10 +76,14 @@ public:
}; };
void updateState(const Data::GroupCall::Participant *participant); void updateState(const Data::GroupCall::Participant *participant);
//UpdateLevelResult updateLevel(float level); void updateLevel(float level);
void updateBlobAnimation(crl::time now);
[[nodiscard]] State state() const { [[nodiscard]] State state() const {
return _state; return _state;
} }
[[nodiscard]] uint32 ssrc() const {
return _ssrc;
}
[[nodiscard]] bool speaking() const { [[nodiscard]] bool speaking() const {
return _speaking; return _speaking;
} }
@ -85,9 +115,12 @@ public:
bool selected, bool selected,
bool actionSelected) override; bool actionSelected) override;
auto generatePaintUserpicCallback() -> PaintRoundImageCallback override;
private: private:
void refreshStatus() override; void refreshStatus() override;
void setSpeaking(bool speaking); void setSpeaking(bool speaking);
void setSsrc(uint32 ssrc);
[[nodiscard]] static State ComputeState( [[nodiscard]] static State ComputeState(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
@ -98,10 +131,12 @@ private:
State _state = State::Inactive; State _state = State::Inactive;
not_null<ChannelData*> _channel; not_null<ChannelData*> _channel;
not_null<const style::IconButton*> _st; not_null<const style::IconButton*> _st;
bool _speaking = false;
//float _level = 0.;
std::unique_ptr<Ui::RippleAnimation> _actionRipple; std::unique_ptr<Ui::RippleAnimation> _actionRipple;
std::unique_ptr<Ui::Paint::Blobs> _blobs;
crl::time _blobsLastTime = 0;
uint32 _ssrc = 0;
float _level = 0.;
bool _speaking = false;
}; };
@ -132,9 +167,9 @@ public:
-> rpl::producer<not_null<UserData*>>; -> rpl::producer<not_null<UserData*>>;
private: private:
[[nodiscard]] std::unique_ptr<PeerListRow> createSelfRow() const; [[nodiscard]] std::unique_ptr<Row> createSelfRow();
[[nodiscard]] std::unique_ptr<PeerListRow> createRow( [[nodiscard]] std::unique_ptr<Row> createRow(
const Data::GroupCall::Participant &participant) const; const Data::GroupCall::Participant &participant);
void prepareRows(not_null<Data::GroupCall*> real); void prepareRows(not_null<Data::GroupCall*> real);
//void repaintByTimer(); //void repaintByTimer();
@ -146,9 +181,10 @@ private:
const Data::GroupCall::Participant &now); const Data::GroupCall::Participant &now);
void updateRow( void updateRow(
not_null<Row*> row, not_null<Row*> row,
const Data::GroupCall::Participant *participant) const; const Data::GroupCall::Participant *participant);
void removeRow(not_null<Row*> row);
void updateRowLevel(not_null<Row*> row, float level);
void checkSpeakingRowPosition(not_null<Row*> row); void checkSpeakingRowPosition(not_null<Row*> row);
//void updateRowLevel(not_null<UserData*> user, float level);
Row *findRow(not_null<UserData*> user) const; Row *findRow(not_null<UserData*> user) const;
[[nodiscard]] Data::GroupCall *resolvedRealCall() const; [[nodiscard]] Data::GroupCall *resolvedRealCall() const;
@ -167,8 +203,8 @@ private:
not_null<QWidget*> _menuParent; not_null<QWidget*> _menuParent;
base::unique_qptr<Ui::PopupMenu> _menu; base::unique_qptr<Ui::PopupMenu> _menu;
//base::flat_map<not_null<UserData*>, crl::time> _repaintByTimer; base::flat_map<uint32, not_null<Row*>> _speakingRowBySsrc;
//base::Timer _repaintTimer; Ui::Animations::Basic _speakingAnimation;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
@ -183,6 +219,7 @@ Row::Row(not_null<ChannelData*> channel, not_null<UserData*> user)
} }
void Row::updateState(const Data::GroupCall::Participant *participant) { void Row::updateState(const Data::GroupCall::Participant *participant) {
setSsrc(participant ? participant->ssrc : 0);
if (!participant) { if (!participant) {
if (peer()->isSelf()) { if (peer()->isSelf()) {
setCustomStatus(tr::lng_group_call_connecting(tr::now)); setCustomStatus(tr::lng_group_call_connecting(tr::now));
@ -193,7 +230,7 @@ void Row::updateState(const Data::GroupCall::Participant *participant) {
setSpeaking(false); setSpeaking(false);
} else if (!participant->muted) { } else if (!participant->muted) {
_state = State::Active; _state = State::Active;
setSpeaking(participant->speaking); setSpeaking(participant->speaking && participant->ssrc != 0);
} else if (participant->canSelfUnmute) { } else if (participant->canSelfUnmute) {
_state = State::Inactive; _state = State::Inactive;
setSpeaking(false); setSpeaking(false);
@ -209,42 +246,49 @@ void Row::setSpeaking(bool speaking) {
return; return;
} }
_speaking = speaking; _speaking = speaking;
if (!_speaking) {
_blobs = nullptr;
}
refreshStatus(); refreshStatus();
//if (!_speaking) {
// updateLevel(0.);
//}
} }
//UpdateLevelResult Row::updateLevel(float level) { void Row::setSsrc(uint32 ssrc) {
// if (_level == level) { _ssrc = ssrc;
// return UpdateLevelResult{ .nextUpdateTime = _markInactiveAt }; }
// }
// const auto now = crl::now(); void Row::updateLevel(float level) {
// const auto stillActive = (now < _markInactiveAt); Expects(_speaking);
// const auto wasActive = (_level >= kLevelThreshold) && stillActive;
// const auto nowActive = (level >= kLevelThreshold); if (!_blobs) {
// if (nowActive) { _blobs = std::make_unique<Ui::Paint::Blobs>(
// _markInactiveAt = now + kLevelActiveTimeout; RowBlobs() | ranges::to_vector,
// if (_state != State::Active) { kLevelDuration,
// _state = State::Active; kMaxLevel);
// _st = ComputeIconStyle(_state); _blobsLastTime = crl::now();
// } }
// } _blobs->setLevel(level + 0.5);
// _level = level; }
// const auto changed = wasActive != (nowActive || stillActive);
// if (!changed) { void Row::updateBlobAnimation(crl::time now) {
// return UpdateLevelResult{ if (_blobs) {
// .levelChanged = true, _blobs->updateLevel(now - _blobsLastTime);
// .nextUpdateTime = _markInactiveAt, _blobsLastTime = now;
// }; }
// } }
// refreshStatus(now);
// return UpdateLevelResult{ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback {
// .levelChanged = true, auto userpic = ensureUserpicView();
// .stateChanged = true, return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
// .nextUpdateTime = _markInactiveAt, if (_blobs) {
// }; const auto shift = QPointF(x + size / 2., y + size / 2.);
//} p.translate(shift);
_blobs->paint(p, st::groupCallLive1);
p.translate(-shift);
p.setOpacity(1.);
}
peer()->paintUserpicLeft(p, userpic, x, y, outerWidth, size);
};
}
void Row::paintAction( void Row::paintAction(
Painter &p, Painter &p,
@ -333,8 +377,15 @@ MembersController::MembersController(
: _call(call) : _call(call)
, _channel(call->channel()) , _channel(call->channel())
, _menuParent(menuParent) { , _menuParent(menuParent) {
//, _repaintTimer([=] { repaintByTimer(); }) {
setupListChangeViewers(call); setupListChangeViewers(call);
_speakingAnimation.init([=](crl::time now) {
for (const auto [ssrc, row] : _speakingRowBySsrc) {
row->updateBlobAnimation(now);
delegate()->peerListUpdateRow(row);
}
return true;
});
} }
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) { void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
@ -362,21 +413,13 @@ void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
} }
}, _lifetime); }, _lifetime);
//call->levelUpdates( call->levelUpdates(
//) | rpl::start_with_next([=](const LevelUpdate &update) { ) | rpl::start_with_next([=](const LevelUpdate &update) {
// const auto findUserBySource = [&](uint32 source) -> UserData* { const auto i = _speakingRowBySsrc.find(update.ssrc);
// if (const auto real = resolvedRealCall()) { if (i != end(_speakingRowBySsrc)) {
// return real->userBySource(source); updateRowLevel(i->second, update.value);
// } }
// return nullptr; }, _lifetime);
// };
// const auto user = update.self
// ? _channel->session().user().get()
// : findUserBySource(update.source);
// if (user) {
// updateRowLevel(user, update.value);
// }
//}, _lifetime);
} }
void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) { void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
@ -402,10 +445,9 @@ void MembersController::subscribeToChanges(not_null<Data::GroupCall*> real) {
if (!update.now) { if (!update.now) {
if (const auto row = findRow(user)) { if (const auto row = findRow(user)) {
if (user->isSelf()) { if (user->isSelf()) {
row->updateState(nullptr); updateRow(row, nullptr);
delegate()->peerListUpdateRow(row);
} else { } else {
delegate()->peerListRemoveRow(row); removeRow(row);
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
} }
} }
@ -424,7 +466,7 @@ void MembersController::updateRow(
} }
updateRow(row, &now); updateRow(row, &now);
} else if (auto row = createRow(now)) { } else if (auto row = createRow(now)) {
if (now.speaking) { if (row->speaking()) {
delegate()->peerListPrependRow(std::move(row)); delegate()->peerListPrependRow(std::move(row));
} else { } else {
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
@ -466,49 +508,49 @@ 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 { const Data::GroupCall::Participant *participant) {
const auto wasSpeaking = row->speaking();
const auto wasSsrc = row->ssrc();
row->updateState(participant); row->updateState(participant);
const auto nowSpeaking = row->speaking();
const auto nowSsrc = row->ssrc();
const auto wasNoSpeaking = _speakingRowBySsrc.empty();
if (wasSsrc == nowSsrc) {
if (nowSpeaking != wasSpeaking) {
if (nowSpeaking) {
_speakingRowBySsrc.emplace(nowSsrc, row);
} else {
_speakingRowBySsrc.remove(nowSsrc);
}
}
} else {
_speakingRowBySsrc.remove(wasSsrc);
if (nowSpeaking) {
Assert(nowSsrc != 0);
_speakingRowBySsrc.emplace(nowSsrc, row);
}
}
const auto nowNoSpeaking = _speakingRowBySsrc.empty();
if (wasNoSpeaking && !nowNoSpeaking) {
_speakingAnimation.start();
} else if (nowNoSpeaking && !wasNoSpeaking) {
_speakingAnimation.stop();
}
delegate()->peerListUpdateRow(row); delegate()->peerListUpdateRow(row);
} }
//void MembersController::updateRowLevel( void MembersController::removeRow(not_null<Row*> row) {
// not_null<UserData*> user, _speakingRowBySsrc.remove(row->ssrc());
// float level) { delegate()->peerListRemoveRow(row);
// if (const auto row = findRow(user)) { }
// const auto result = row->updateLevel(level);
// if (result.stateChanged) {
// delegate()->peerListUpdateRow(row);
// }
// if (result.nextUpdateTime) {
// _repaintByTimer[user] = result.nextUpdateTime;
// if (!_repaintTimer.isActive()) {
// _repaintTimer.callOnce(kLevelActiveTimeout);
// }
// } else if (_repaintByTimer.remove(user) && _repaintByTimer.empty()) {
// _repaintTimer.cancel();
// }
// }
//}
//void MembersController::repaintByTimer() { void MembersController::updateRowLevel(
// const auto now = crl::now(); not_null<Row*> row,
// auto next = crl::time(0); float level) {
// for (auto i = begin(_repaintByTimer); i != end(_repaintByTimer);) { row->updateLevel(level);
// if (i->second > now) { }
// if (!next || next > i->second) {
// next = i->second;
// }
// } else if (const auto row = findRow(i->first)) {
// delegate()->peerListUpdateRow(row);
// i = _repaintByTimer.erase(i);
// continue;
// }
// ++i;
// }
// if (next) {
// _repaintTimer.callOnce(next - now);
// }
//}
Row *MembersController::findRow(not_null<UserData*> user) const { Row *MembersController::findRow(not_null<UserData*> user) const {
return static_cast<Row*>(delegate()->peerListFindRow(user->id)); return static_cast<Row*>(delegate()->peerListFindRow(user->id));
@ -564,7 +606,7 @@ void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
++i; ++i;
} else { } else {
changed = true; changed = true;
delegate()->peerListRemoveRow(row); removeRow(static_cast<Row*>(row.get()));
--count; --count;
} }
} }
@ -708,15 +750,15 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
return result; return result;
} }
std::unique_ptr<PeerListRow> MembersController::createSelfRow() const { std::unique_ptr<Row> MembersController::createSelfRow() {
const auto self = _channel->session().user(); const auto self = _channel->session().user();
auto result = std::make_unique<Row>(_channel, self); auto result = std::make_unique<Row>(_channel, self);
updateRow(result.get(), nullptr); updateRow(result.get(), nullptr);
return result; return result;
} }
std::unique_ptr<PeerListRow> MembersController::createRow( std::unique_ptr<Row> MembersController::createRow(
const Data::GroupCall::Participant &participant) const { const Data::GroupCall::Participant &participant) {
auto result = std::make_unique<Row>(_channel, participant.user); auto result = std::make_unique<Row>(_channel, participant.user);
updateRow(result.get(), &participant); updateRow(result.get(), &participant);
return result; return result;

View file

@ -31,7 +31,7 @@ GroupCall::GroupCall(
} }
GroupCall::~GroupCall() { GroupCall::~GroupCall() {
api().request(_unknownSourcesRequestId).cancel(); api().request(_unknownSsrcsRequestId).cancel();
api().request(_participantsRequestId).cancel(); api().request(_participantsRequestId).cancel();
api().request(_reloadRequestId).cancel(); api().request(_reloadRequestId).cancel();
} }
@ -65,7 +65,7 @@ void GroupCall::requestParticipants() {
_participantsRequestId = api().request(MTPphone_GetGroupParticipants( _participantsRequestId = api().request(MTPphone_GetGroupParticipants(
input(), input(),
MTP_vector<MTPint>(), // ids MTP_vector<MTPint>(), // ids
MTP_vector<MTPint>(), // sources MTP_vector<MTPint>(), // ssrcs
MTP_string(_nextOffset), MTP_string(_nextOffset),
MTP_int(kRequestPerPage) MTP_int(kRequestPerPage)
)).done([=](const MTPphone_GroupParticipants &result) { )).done([=](const MTPphone_GroupParticipants &result) {
@ -105,9 +105,9 @@ bool GroupCall::participantsLoaded() const {
return _allReceived; return _allReceived;
} }
UserData *GroupCall::userBySource(uint32 source) const { UserData *GroupCall::userBySsrc(uint32 ssrc) const {
const auto i = _userBySource.find(source); const auto i = _userBySsrc.find(ssrc);
return (i != end(_userBySource)) ? i->second.get() : nullptr; return (i != end(_userBySsrc)) ? i->second.get() : nullptr;
} }
rpl::producer<> GroupCall::participantsSliceAdded() { rpl::producer<> GroupCall::participantsSliceAdded() {
@ -163,7 +163,7 @@ void GroupCall::reload() {
result.match([&](const MTPDphone_groupCall &data) { result.match([&](const MTPDphone_groupCall &data) {
_channel->owner().processUsers(data.vusers()); _channel->owner().processUsers(data.vusers());
_participants.clear(); _participants.clear();
_userBySource.clear(); _userBySsrc.clear();
applyParticipantsSlice( applyParticipantsSlice(
data.vparticipants().v, data.vparticipants().v,
ApplySliceSource::SliceLoaded); ApplySliceSource::SliceLoaded);
@ -194,7 +194,7 @@ void GroupCall::applyParticipantsSlice(
auto update = ParticipantUpdate{ auto update = ParticipantUpdate{
.was = *i, .was = *i,
}; };
_userBySource.erase(i->source); _userBySsrc.erase(i->ssrc);
_participants.erase(i); _participants.erase(i);
if (sliceSource != ApplySliceSource::SliceLoaded) { if (sliceSource != ApplySliceSource::SliceLoaded) {
_participantUpdates.fire(std::move(update)); _participantUpdates.fire(std::move(update));
@ -212,20 +212,20 @@ void GroupCall::applyParticipantsSlice(
.user = user, .user = user,
.date = data.vdate().v, .date = data.vdate().v,
.lastActive = was ? was->lastActive : 0, .lastActive = was ? was->lastActive : 0,
.source = uint32(data.vsource().v), .ssrc = uint32(data.vsource().v),
.speaking = !data.is_muted() && (was ? was->speaking : false), .speaking = !data.is_muted() && (was ? was->speaking : false),
.muted = data.is_muted(), .muted = data.is_muted(),
.canSelfUnmute = !data.is_muted() || data.is_can_self_unmute(), .canSelfUnmute = !data.is_muted() || data.is_can_self_unmute(),
}; };
if (i == end(_participants)) { if (i == end(_participants)) {
_userBySource.emplace(value.source, user); _userBySsrc.emplace(value.ssrc, user);
_participants.push_back(value); _participants.push_back(value);
_channel->owner().unregisterInvitedToCallUser(_id, user); _channel->owner().unregisterInvitedToCallUser(_id, user);
++changedCount; ++changedCount;
} else { } else {
if (i->source != value.source) { if (i->ssrc != value.ssrc) {
_userBySource.erase(i->source); _userBySsrc.erase(i->ssrc);
_userBySource.emplace(value.source, user); _userBySsrc.emplace(value.ssrc, user);
} }
*i = value; *i = value;
} }
@ -271,11 +271,11 @@ void GroupCall::applyParticipantsMutes(
} }
} }
void GroupCall::applyLastSpoke(uint32 source, crl::time when, crl::time now) { void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) {
const auto i = _userBySource.find(source); const auto i = _userBySsrc.find(ssrc);
if (i == end(_userBySource)) { if (i == end(_userBySsrc)) {
_unknownSpokenSources.emplace(source, when); _unknownSpokenSsrcs.emplace(ssrc, when);
requestUnknownSources(); requestUnknownSsrcs();
return; return;
} }
const auto j = ranges::find(_participants, i->second, &Participant::user); const auto j = ranges::find(_participants, i->second, &Participant::user);
@ -292,32 +292,32 @@ void GroupCall::applyLastSpoke(uint32 source, crl::time when, crl::time now) {
} }
} }
void GroupCall::requestUnknownSources() { void GroupCall::requestUnknownSsrcs() {
if (_unknownSourcesRequestId || _unknownSpokenSources.empty()) { if (_unknownSsrcsRequestId || _unknownSpokenSsrcs.empty()) {
return; return;
} }
const auto sources = [&] { const auto ssrcs = [&] {
if (_unknownSpokenSources.size() < kRequestPerPage) { if (_unknownSpokenSsrcs.size() < kRequestPerPage) {
return base::take(_unknownSpokenSources); return base::take(_unknownSpokenSsrcs);
} }
auto result = base::flat_map<uint32, crl::time>(); auto result = base::flat_map<uint32, crl::time>();
result.reserve(kRequestPerPage); result.reserve(kRequestPerPage);
while (result.size() < kRequestPerPage) { while (result.size() < kRequestPerPage) {
const auto [source, when] = _unknownSpokenSources.back(); const auto [ssrc, when] = _unknownSpokenSsrcs.back();
result.emplace(source, when); result.emplace(ssrc, when);
_unknownSpokenSources.erase(_unknownSpokenSources.end() - 1); _unknownSpokenSsrcs.erase(_unknownSpokenSsrcs.end() - 1);
} }
return result; return result;
}(); }();
auto sourceInputs = QVector<MTPint>(); auto inputs = QVector<MTPint>();
sourceInputs.reserve(sources.size()); inputs.reserve(ssrcs.size());
for (const auto [source, when] : sources) { for (const auto [ssrc, when] : ssrcs) {
sourceInputs.push_back(MTP_int(source)); inputs.push_back(MTP_int(ssrc));
} }
_unknownSourcesRequestId = api().request(MTPphone_GetGroupParticipants( _unknownSsrcsRequestId = api().request(MTPphone_GetGroupParticipants(
input(), input(),
MTP_vector<MTPint>(), // ids MTP_vector<MTPint>(), // ids
MTP_vector<MTPint>(sourceInputs), MTP_vector<MTPint>(inputs),
MTP_string(QString()), MTP_string(QString()),
MTP_int(kRequestPerPage) MTP_int(kRequestPerPage)
)).done([=](const MTPphone_GroupParticipants &result) { )).done([=](const MTPphone_GroupParticipants &result) {
@ -327,19 +327,19 @@ void GroupCall::requestUnknownSources() {
data.vparticipants().v, data.vparticipants().v,
ApplySliceSource::UnknownLoaded); ApplySliceSource::UnknownLoaded);
}); });
_unknownSourcesRequestId = 0; _unknownSsrcsRequestId = 0;
const auto now = crl::now(); const auto now = crl::now();
for (const auto [source, when] : sources) { for (const auto [ssrc, when] : ssrcs) {
applyLastSpoke(source, when, now); applyLastSpoke(ssrc, when, now);
_unknownSpokenSources.remove(source); _unknownSpokenSsrcs.remove(ssrc);
} }
requestUnknownSources(); requestUnknownSsrcs();
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_unknownSourcesRequestId = 0; _unknownSsrcsRequestId = 0;
for (const auto [source, when] : sources) { for (const auto [ssrc, when] : ssrcs) {
_unknownSpokenSources.remove(source); _unknownSpokenSsrcs.remove(ssrc);
} }
requestUnknownSources(); requestUnknownSsrcs();
}).send(); }).send();
} }

View file

@ -27,7 +27,7 @@ public:
not_null<UserData*> user; not_null<UserData*> user;
TimeId date = 0; TimeId date = 0;
TimeId lastActive = 0; TimeId lastActive = 0;
uint32 source = 0; uint32 ssrc = 0;
bool speaking = false; bool speaking = false;
bool muted = false; bool muted = false;
bool canSelfUnmute = false; bool canSelfUnmute = false;
@ -41,7 +41,7 @@ public:
-> const std::vector<Participant> &; -> const std::vector<Participant> &;
void requestParticipants(); void requestParticipants();
[[nodiscard]] bool participantsLoaded() const; [[nodiscard]] bool participantsLoaded() const;
[[nodiscard]] UserData *userBySource(uint32 source) const; [[nodiscard]] UserData *userBySsrc(uint32 ssrc) const;
[[nodiscard]] rpl::producer<> participantsSliceAdded(); [[nodiscard]] rpl::producer<> participantsSliceAdded();
[[nodiscard]] rpl::producer<ParticipantUpdate> participantUpdated() const; [[nodiscard]] rpl::producer<ParticipantUpdate> participantUpdated() const;
@ -50,7 +50,7 @@ public:
void applyUpdate(const MTPDupdateGroupCallParticipants &update); void applyUpdate(const MTPDupdateGroupCallParticipants &update);
void applyUpdateChecked( void applyUpdateChecked(
const MTPDupdateGroupCallParticipants &update); const MTPDupdateGroupCallParticipants &update);
void applyLastSpoke(uint32 source, crl::time when, crl::time now); void applyLastSpoke(uint32 ssrc, crl::time when, crl::time now);
[[nodiscard]] int fullCount() const; [[nodiscard]] int fullCount() const;
[[nodiscard]] rpl::producer<int> fullCountValue() const; [[nodiscard]] rpl::producer<int> fullCountValue() const;
@ -75,7 +75,7 @@ private:
ApplySliceSource sliceSource); ApplySliceSource sliceSource);
void applyParticipantsMutes( void applyParticipantsMutes(
const MTPDupdateGroupCallParticipants &update); const MTPDupdateGroupCallParticipants &update);
void requestUnknownSources(); void requestUnknownSsrcs();
const not_null<ChannelData*> _channel; const not_null<ChannelData*> _channel;
const uint64 _id = 0; const uint64 _id = 0;
@ -86,12 +86,12 @@ private:
mtpRequestId _reloadRequestId = 0; mtpRequestId _reloadRequestId = 0;
std::vector<Participant> _participants; std::vector<Participant> _participants;
base::flat_map<uint32, not_null<UserData*>> _userBySource; base::flat_map<uint32, not_null<UserData*>> _userBySsrc;
QString _nextOffset; QString _nextOffset;
rpl::variable<int> _fullCount = 0; rpl::variable<int> _fullCount = 0;
base::flat_map<uint32, crl::time> _unknownSpokenSources; base::flat_map<uint32, crl::time> _unknownSpokenSsrcs;
mtpRequestId _unknownSourcesRequestId = 0; mtpRequestId _unknownSsrcsRequestId = 0;
rpl::event_stream<ParticipantUpdate> _participantUpdates; rpl::event_stream<ParticipantUpdate> _participantUpdates;
rpl::event_stream<> _participantsSliceAdded; rpl::event_stream<> _participantsSliceAdded;