Switch between videos by left click.

This commit is contained in:
John Preston 2021-05-11 13:35:04 +04:00
parent 7e8d1f7974
commit 3a321d64f6
5 changed files with 173 additions and 134 deletions

View file

@ -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<Webrtc::MediaDevices> 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<PeerData*> 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<Data::GroupCall*> 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<Data::GroupCall*> 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<MTPGroupCallParticipant>(
@ -2013,17 +2007,15 @@ bool GroupCall::mediaChannelDescriptionsFill(
const auto addVideoChannel = [&](
not_null<PeerData*> participantPeer,
const auto field) {
const auto i = ranges::find(
existing,
participantPeer,
&Data::GroupCallParticipant::peer);
Assert(i != end(existing));
Assert(i->videoParams != nullptr);
const auto &params = i->videoParams.get()->*field;
const auto participant = real->participantByPeer(
participantPeer);
Assert(participant != nullptr);
Assert(participant->videoParams != nullptr);
const auto &params = 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<int, not_null<UserData*>> 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<UserData*> user) {
return !invited.contains(user) && !ranges::contains(
participants,
user,
&Data::GroupCallParticipant::peer);
return !invited.contains(user) && !real->participantByPeer(user);
});
auto count = 0;

View file

@ -289,7 +289,7 @@ public:
[[nodiscard]] rpl::producer<bool> 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<VideoEndpoint> {
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<PeerData*> participantPeer,
@ -539,6 +541,7 @@ private:
rpl::variable<bool> _videoEndpointPinned;
std::unique_ptr<Webrtc::VideoTrack> _videoLargeTrackWrap;
rpl::variable<LargeTrack> _videoLargeTrack;
crl::time _videoLargeShowTime = 0;
base::flat_map<uint32, Data::LastSpokeTimes> _lastSpoke;
rpl::event_stream<Group::RejoinEvent> _rejoinEvents;
rpl::event_stream<> _allowedToSpeakNotifications;

View file

@ -159,6 +159,8 @@ private:
void setRowVideoEndpoint(
not_null<Row*> row,
const std::string &endpoint);
bool toggleRowVideo(not_null<PeerListRow*> row);
void showRowMenu(not_null<PeerListRow*> 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<PeerData*> participantPeer) const {
void Members::Controller::prepareRows(not_null<Data::GroupCall*> 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<Data::GroupCall*> 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<Data::GroupCall*> 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<PeerListRow*> row) {
if (!toggleRowVideo(row)) {
showRowMenu(row);
}
}
void Members::Controller::showRowMenu(not_null<PeerListRow*> row) {
delegate()->peerListShowRowMenu(row, [=](not_null<Ui::PopupMenu*> menu) {
if (!_menu || _menu.get() != menu) {
return;
@ -1103,9 +1095,51 @@ void Members::Controller::rowClicked(not_null<PeerListRow*> row) {
});
}
bool Members::Controller::toggleRowVideo(not_null<PeerListRow*> 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<PeerListRow*> row) {
rowClicked(row);
showRowMenu(row);
}
base::unique_qptr<Ui::PopupMenu> Members::Controller::rowContextMenu(
@ -1195,26 +1229,20 @@ base::unique_qptr<Ui::PopupMenu> 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);
}
}
}

View file

@ -229,6 +229,17 @@ PeerData *GroupCall::participantPeerByScreenSsrc(uint32 ssrc) const {
: nullptr;
}
const GroupCallParticipant *GroupCall::participantByPeer(
not_null<PeerData*> peer) const {
return const_cast<GroupCall*>(this)->findParticipant(peer);
}
GroupCallParticipant *GroupCall::findParticipant(
not_null<PeerData*> 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,
});
}
}

View file

@ -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<PeerData*> 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<PeerData*> peer);
void emplaceVideoSsrcs(const Participant &participant);
void eraseVideoSsrcs(const Participant &participant);