mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Add video placeholder if can't receive it.
This commit is contained in:
parent
b2bf8244dd
commit
f18e157e46
16 changed files with 249 additions and 79 deletions
|
@ -2058,6 +2058,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_group_call_ptt_delay" = "Push to Talk release delay: {delay}";
|
"lng_group_call_ptt_delay" = "Push to Talk release delay: {delay}";
|
||||||
"lng_group_call_share" = "Share Invite Link";
|
"lng_group_call_share" = "Share Invite Link";
|
||||||
"lng_group_call_noise_suppression" = "Enable Noise Suppression";
|
"lng_group_call_noise_suppression" = "Enable Noise Suppression";
|
||||||
|
"lng_group_call_limit#one" = "Video is only available\nfor the first {count} member";
|
||||||
|
"lng_group_call_limit#other" = "Video is only available\nfor the first {count} members";
|
||||||
"lng_group_call_share_speaker" = "Users with this link can speak";
|
"lng_group_call_share_speaker" = "Users with this link can speak";
|
||||||
"lng_group_call_copy_speaker_link" = "Copy Speaker Link";
|
"lng_group_call_copy_speaker_link" = "Copy Speaker Link";
|
||||||
"lng_group_call_copy_listener_link" = "Copy Listener Link";
|
"lng_group_call_copy_listener_link" = "Copy Listener Link";
|
||||||
|
|
|
@ -1229,6 +1229,9 @@ groupCallVideoTile: GroupCallVideoTile {
|
||||||
|
|
||||||
groupCallVideoSmallSkip: 4px;
|
groupCallVideoSmallSkip: 4px;
|
||||||
groupCallVideoLargeSkip: 6px;
|
groupCallVideoLargeSkip: 6px;
|
||||||
|
groupCallVideoPlaceholderHeight: 212px;
|
||||||
|
groupCallVideoPlaceholderIconTop: 50px;
|
||||||
|
groupCallVideoPlaceholderTextTop: 120px;
|
||||||
|
|
||||||
groupCallTooltip: Tooltip(defaultTooltip) {
|
groupCallTooltip: Tooltip(defaultTooltip) {
|
||||||
textBg: groupCallMembersBg;
|
textBg: groupCallMembersBg;
|
||||||
|
|
|
@ -821,6 +821,9 @@ void GroupCall::setState(State state) {
|
||||||
if (const auto call = _peer->groupCall(); call && call->id() == _id) {
|
if (const auto call = _peer->groupCall(); call && call->id() == _id) {
|
||||||
call->setInCall();
|
call->setInCall();
|
||||||
}
|
}
|
||||||
|
if (!videoIsWorking()) {
|
||||||
|
refreshHasNotShownVideo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false
|
if (false
|
||||||
|
@ -1043,6 +1046,9 @@ void GroupCall::markEndpointActive(
|
||||||
bool paused) {
|
bool paused) {
|
||||||
if (!endpoint) {
|
if (!endpoint) {
|
||||||
return;
|
return;
|
||||||
|
} else if (active && !videoIsWorking()) {
|
||||||
|
refreshHasNotShownVideo();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const auto i = _activeVideoTracks.find(endpoint);
|
const auto i = _activeVideoTracks.find(endpoint);
|
||||||
const auto changed = active
|
const auto changed = active
|
||||||
|
@ -1067,26 +1073,34 @@ void GroupCall::markEndpointActive(
|
||||||
.peer = endpoint.peer,
|
.peer = endpoint.peer,
|
||||||
}).first;
|
}).first;
|
||||||
const auto track = i->second.track.get();
|
const auto track = i->second.track.get();
|
||||||
if (!track->frameSize().isEmpty()
|
|
||||||
|| track->state() == Webrtc::VideoState::Paused) {
|
track->renderNextFrame(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
auto &activeTrack = _activeVideoTracks[endpoint];
|
||||||
|
const auto size = track->frameSize();
|
||||||
|
if (size.isEmpty()) {
|
||||||
|
track->markFrameShown();
|
||||||
|
} else if (!activeTrack.shown) {
|
||||||
|
activeTrack.shown = true;
|
||||||
|
markTrackShown(endpoint, true);
|
||||||
|
}
|
||||||
|
activeTrack.trackSize = size;
|
||||||
|
}, i->second.lifetime);
|
||||||
|
|
||||||
|
const auto size = track->frameSize();
|
||||||
|
i->second.trackSize = size;
|
||||||
|
if (!size.isEmpty() || paused) {
|
||||||
|
i->second.shown = true;
|
||||||
shown = true;
|
shown = true;
|
||||||
} else {
|
} else {
|
||||||
auto hasFrame = track->renderNextFrame() | rpl::map([=] {
|
track->stateValue(
|
||||||
return !track->frameSize().isEmpty();
|
) | rpl::filter([=](Webrtc::VideoState state) {
|
||||||
});
|
return (state == Webrtc::VideoState::Paused)
|
||||||
auto isPaused = track->stateValue(
|
&& !_activeVideoTracks[endpoint].shown;
|
||||||
) | rpl::map([=](Webrtc::VideoState state) {
|
|
||||||
return (state == Webrtc::VideoState::Paused);
|
|
||||||
});
|
|
||||||
rpl::merge(
|
|
||||||
std::move(hasFrame),
|
|
||||||
std::move(isPaused)
|
|
||||||
) | rpl::filter([=](bool shouldShow) {
|
|
||||||
return shouldShow;
|
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
_activeVideoTracks[endpoint].shownTrackingLifetime.destroy();
|
_activeVideoTracks[endpoint].shown = true;
|
||||||
markTrackShown(endpoint, true);
|
markTrackShown(endpoint, true);
|
||||||
}, i->second.shownTrackingLifetime);
|
}, i->second.lifetime);
|
||||||
}
|
}
|
||||||
addVideoOutput(i->first.id, { track->sink() });
|
addVideoOutput(i->first.id, { track->sink() });
|
||||||
} else {
|
} else {
|
||||||
|
@ -1109,10 +1123,11 @@ void GroupCall::markTrackShown(const VideoEndpoint &endpoint, bool shown) {
|
||||||
const auto changed = shown
|
const auto changed = shown
|
||||||
? _shownVideoTracks.emplace(endpoint).second
|
? _shownVideoTracks.emplace(endpoint).second
|
||||||
: _shownVideoTracks.remove(endpoint);
|
: _shownVideoTracks.remove(endpoint);
|
||||||
if (changed) {
|
if (!changed) {
|
||||||
_videoStreamShownUpdates.fire_copy({ endpoint, shown });
|
return;
|
||||||
}
|
}
|
||||||
if (shown && changed && endpoint.type == VideoEndpointType::Screen) {
|
_videoStreamShownUpdates.fire_copy({ endpoint, shown });
|
||||||
|
if (shown && endpoint.type == VideoEndpointType::Screen) {
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
if (_shownVideoTracks.contains(endpoint)) {
|
if (_shownVideoTracks.contains(endpoint)) {
|
||||||
pinVideoEndpoint(endpoint);
|
pinVideoEndpoint(endpoint);
|
||||||
|
@ -2431,21 +2446,37 @@ void GroupCall::updateRequestedVideoChannelsDelayed() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GroupCall::refreshHasNotShownVideo() {
|
||||||
|
if (!_joinState.ssrc || hasNotShownVideo()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto real = lookupReal();
|
||||||
|
Assert(real != nullptr);
|
||||||
|
|
||||||
|
const auto hasVideo = [&](const Data::GroupCallParticipant &data) {
|
||||||
|
return (data.peer != _joinAs)
|
||||||
|
&& (!GetCameraEndpoint(data.videoParams).empty()
|
||||||
|
|| !GetScreenEndpoint(data.videoParams).empty());
|
||||||
|
};
|
||||||
|
_hasNotShownVideo = _joinState.ssrc
|
||||||
|
&& ranges::any_of(real->participants(), hasVideo);
|
||||||
|
}
|
||||||
|
|
||||||
void GroupCall::fillActiveVideoEndpoints() {
|
void GroupCall::fillActiveVideoEndpoints() {
|
||||||
const auto real = lookupReal();
|
const auto real = lookupReal();
|
||||||
Assert(real != nullptr);
|
Assert(real != nullptr);
|
||||||
|
|
||||||
if (const auto participant = real->participantByPeer(_joinAs)) {
|
const auto me = real->participantByPeer(_joinAs);
|
||||||
_videoIsWorking = participant->videoJoined;
|
if (me && me->videoJoined) {
|
||||||
|
_videoIsWorking = true;
|
||||||
|
_hasNotShownVideo = false;
|
||||||
} else {
|
} else {
|
||||||
|
refreshHasNotShownVideo();
|
||||||
_videoIsWorking = false;
|
_videoIsWorking = false;
|
||||||
}
|
|
||||||
if (!videoIsWorking()) {
|
|
||||||
toggleVideo(false);
|
toggleVideo(false);
|
||||||
toggleScreenSharing(std::nullopt);
|
toggleScreenSharing(std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &participants = real->participants();
|
|
||||||
const auto &large = _videoEndpointLarge.current();
|
const auto &large = _videoEndpointLarge.current();
|
||||||
auto largeFound = false;
|
auto largeFound = false;
|
||||||
auto endpoints = _activeVideoTracks | ranges::views::transform([](
|
auto endpoints = _activeVideoTracks | ranges::views::transform([](
|
||||||
|
@ -2469,7 +2500,7 @@ void GroupCall::fillActiveVideoEndpoints() {
|
||||||
};
|
};
|
||||||
using Type = VideoEndpointType;
|
using Type = VideoEndpointType;
|
||||||
if (_videoIsWorking.current()) {
|
if (_videoIsWorking.current()) {
|
||||||
for (const auto &participant : participants) {
|
for (const auto &participant : real->participants()) {
|
||||||
const auto camera = GetCameraEndpoint(participant.videoParams);
|
const auto camera = GetCameraEndpoint(participant.videoParams);
|
||||||
if (camera != _cameraEndpoint
|
if (camera != _cameraEndpoint
|
||||||
&& camera != _screenEndpoint
|
&& camera != _screenEndpoint
|
||||||
|
@ -2485,7 +2516,6 @@ void GroupCall::fillActiveVideoEndpoints() {
|
||||||
feedOne({ Type::Screen, participant.peer, screen }, paused);
|
feedOne({ Type::Screen, participant.peer, screen }, paused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto pausedState = Webrtc::VideoState::Paused;
|
|
||||||
feedOne(
|
feedOne(
|
||||||
{ Type::Camera, _joinAs, cameraSharingEndpoint() },
|
{ Type::Camera, _joinAs, cameraSharingEndpoint() },
|
||||||
isCameraPaused());
|
isCameraPaused());
|
||||||
|
|
|
@ -324,9 +324,11 @@ public:
|
||||||
|
|
||||||
struct VideoTrack {
|
struct VideoTrack {
|
||||||
std::unique_ptr<Webrtc::VideoTrack> track;
|
std::unique_ptr<Webrtc::VideoTrack> track;
|
||||||
|
rpl::variable<QSize> trackSize;
|
||||||
PeerData *peer = nullptr;
|
PeerData *peer = nullptr;
|
||||||
rpl::lifetime shownTrackingLifetime;
|
rpl::lifetime lifetime;
|
||||||
Group::VideoQuality quality = Group::VideoQuality();
|
Group::VideoQuality quality = Group::VideoQuality();
|
||||||
|
bool shown = false;
|
||||||
|
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return (track != nullptr);
|
return (track != nullptr);
|
||||||
|
@ -366,6 +368,12 @@ public:
|
||||||
[[nodiscard]] rpl::producer<bool> videoIsWorkingValue() const {
|
[[nodiscard]] rpl::producer<bool> videoIsWorkingValue() const {
|
||||||
return _videoIsWorking.value();
|
return _videoIsWorking.value();
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] bool hasNotShownVideo() const {
|
||||||
|
return _hasNotShownVideo.current();
|
||||||
|
}
|
||||||
|
[[nodiscard]] rpl::producer<bool> hasNotShownVideoValue() const {
|
||||||
|
return _hasNotShownVideo.value();
|
||||||
|
}
|
||||||
|
|
||||||
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
void setCurrentAudioDevice(bool input, const QString &deviceId);
|
||||||
void setCurrentVideoDevice(const QString &deviceId);
|
void setCurrentVideoDevice(const QString &deviceId);
|
||||||
|
@ -514,6 +522,7 @@ private:
|
||||||
void updateRequestedVideoChannels();
|
void updateRequestedVideoChannels();
|
||||||
void updateRequestedVideoChannelsDelayed();
|
void updateRequestedVideoChannelsDelayed();
|
||||||
void fillActiveVideoEndpoints();
|
void fillActiveVideoEndpoints();
|
||||||
|
void refreshHasNotShownVideo();
|
||||||
|
|
||||||
void editParticipant(
|
void editParticipant(
|
||||||
not_null<PeerData*> participantPeer,
|
not_null<PeerData*> participantPeer,
|
||||||
|
@ -570,6 +579,7 @@ private:
|
||||||
rpl::variable<MuteState> _muted = MuteState::Muted;
|
rpl::variable<MuteState> _muted = MuteState::Muted;
|
||||||
rpl::variable<bool> _canManage = false;
|
rpl::variable<bool> _canManage = false;
|
||||||
rpl::variable<bool> _videoIsWorking = false;
|
rpl::variable<bool> _videoIsWorking = false;
|
||||||
|
rpl::variable<bool> _hasNotShownVideo = false;
|
||||||
bool _initialMuteStateSent = false;
|
bool _initialMuteStateSent = false;
|
||||||
bool _acceptFields = false;
|
bool _acceptFields = false;
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "core/application.h" // Core::App().domain, .activeWindow.
|
#include "core/application.h" // Core::App().domain, .activeWindow.
|
||||||
#include "main/main_domain.h" // Core::App().domain().activate.
|
#include "main/main_domain.h" // Core::App().domain().activate.
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "main/main_account.h" // account().appConfig().
|
||||||
|
#include "main/main_app_config.h" // appConfig().get<double>().
|
||||||
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
|
#include "boxes/peers/edit_participants_box.h" // SubscribeToMigration.
|
||||||
#include "window/window_controller.h" // Controller::sessionController.
|
#include "window/window_controller.h" // Controller::sessionController.
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
@ -41,9 +43,110 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kKeepRaisedHandStatusDuration = 3 * crl::time(1000);
|
constexpr auto kKeepRaisedHandStatusDuration = 3 * crl::time(1000);
|
||||||
constexpr auto kShadowMaxAlpha = 74;
|
constexpr auto kShadowMaxAlpha = 74;
|
||||||
|
constexpr auto kUserpicSizeForBlur = 40;
|
||||||
|
constexpr auto kUserpicBlurRadius = 8;
|
||||||
|
|
||||||
using Row = MembersRow;
|
using Row = MembersRow;
|
||||||
|
|
||||||
|
void SetupVideoPlaceholder(
|
||||||
|
not_null<Ui::RpWidget*> widget,
|
||||||
|
not_null<PeerData*> chat) {
|
||||||
|
struct State {
|
||||||
|
QImage blurred;
|
||||||
|
QImage rounded;
|
||||||
|
InMemoryKey key = {};
|
||||||
|
std::shared_ptr<Data::CloudImageView> view;
|
||||||
|
qint64 blurredCacheKey = 0;
|
||||||
|
};
|
||||||
|
const auto state = widget->lifetime().make_state<State>();
|
||||||
|
const auto refreshBlurred = [=] {
|
||||||
|
const auto key = chat->userpicUniqueKey(state->view);
|
||||||
|
if (state->key == key && !state->blurred.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
constexpr auto size = kUserpicSizeForBlur;
|
||||||
|
state->key = key;
|
||||||
|
state->blurred = QImage(
|
||||||
|
QSize(size, size),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
{
|
||||||
|
auto p = Painter(&state->blurred);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
chat->paintUserpicSquare(p, state->view, 0, 0, size);
|
||||||
|
}
|
||||||
|
state->blurred = Images::BlurLargeImage(
|
||||||
|
std::move(state->blurred),
|
||||||
|
kUserpicBlurRadius);
|
||||||
|
widget->update();
|
||||||
|
};
|
||||||
|
const auto refreshRounded = [=](QSize size) {
|
||||||
|
refreshBlurred();
|
||||||
|
const auto key = state->blurred.cacheKey();
|
||||||
|
if (state->rounded.size() == size && state->blurredCacheKey == key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state->blurredCacheKey = key;
|
||||||
|
state->rounded = Images::prepare(
|
||||||
|
state->blurred,
|
||||||
|
size.width(),
|
||||||
|
size.width(), // Square
|
||||||
|
Images::Option::Smooth,
|
||||||
|
size.width(),
|
||||||
|
size.height());
|
||||||
|
{
|
||||||
|
auto p = QPainter(&state->rounded);
|
||||||
|
p.fillRect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
size.width(),
|
||||||
|
size.height(),
|
||||||
|
QColor(0, 0, 0, Viewport::kShadowMaxAlpha));
|
||||||
|
}
|
||||||
|
state->rounded = Images::prepare(
|
||||||
|
std::move(state->rounded),
|
||||||
|
size.width(),
|
||||||
|
size.height(),
|
||||||
|
(Images::Option::RoundedLarge | Images::Option::RoundedAll),
|
||||||
|
size.width(),
|
||||||
|
size.height());
|
||||||
|
};
|
||||||
|
chat->loadUserpic();
|
||||||
|
refreshBlurred();
|
||||||
|
|
||||||
|
widget->paintRequest(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
const auto size = QSize(
|
||||||
|
widget->width(),
|
||||||
|
widget->height() - st::groupCallVideoSmallSkip);
|
||||||
|
refreshRounded(size * cIntRetinaFactor());
|
||||||
|
|
||||||
|
auto p = QPainter(widget);
|
||||||
|
const auto inner = QRect(QPoint(), size);
|
||||||
|
p.drawImage(inner, state->rounded);
|
||||||
|
st::groupCallPaused.paint(
|
||||||
|
p,
|
||||||
|
(size.width() - st::groupCallPaused.width()) / 2,
|
||||||
|
st::groupCallVideoPlaceholderIconTop,
|
||||||
|
size.width());
|
||||||
|
|
||||||
|
const auto skip = st::groupCallVideoLargeSkip;
|
||||||
|
const auto limit = chat->session().account().appConfig().get<double>(
|
||||||
|
"groupcall_video_participants_max",
|
||||||
|
30.);
|
||||||
|
p.setPen(st::groupCallVideoTextFg);
|
||||||
|
const auto text = QRect(
|
||||||
|
skip,
|
||||||
|
st::groupCallVideoPlaceholderTextTop,
|
||||||
|
(size.width() - 2 * skip),
|
||||||
|
size.height() - st::groupCallVideoPlaceholderTextTop);
|
||||||
|
p.setFont(st::semiboldFont);
|
||||||
|
p.drawText(
|
||||||
|
text,
|
||||||
|
tr::lng_group_call_limit(tr::now, lt_count, int(limit)),
|
||||||
|
style::al_top);
|
||||||
|
}, widget->lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class Members::Controller final
|
class Members::Controller final
|
||||||
|
@ -1467,6 +1570,7 @@ Members::Members(
|
||||||
, _layout(_scroll->setOwnedWidget(
|
, _layout(_scroll->setOwnedWidget(
|
||||||
object_ptr<Ui::VerticalLayout>(_scroll.data())))
|
object_ptr<Ui::VerticalLayout>(_scroll.data())))
|
||||||
, _videoWrap(_layout->add(object_ptr<Ui::RpWidget>(_layout.get())))
|
, _videoWrap(_layout->add(object_ptr<Ui::RpWidget>(_layout.get())))
|
||||||
|
, _videoPlaceholder(std::make_unique<Ui::RpWidget>(_videoWrap.get()))
|
||||||
, _viewport(
|
, _viewport(
|
||||||
std::make_unique<Viewport>(
|
std::make_unique<Viewport>(
|
||||||
_videoWrap.get(),
|
_videoWrap.get(),
|
||||||
|
@ -1704,11 +1808,27 @@ void Members::trackViewportGeometry() {
|
||||||
_scroll->scrollTopValue(
|
_scroll->scrollTopValue(
|
||||||
) | rpl::skip(1) | rpl::start_with_next(move, _viewport->lifetime());
|
) | rpl::skip(1) | rpl::start_with_next(move, _viewport->lifetime());
|
||||||
|
|
||||||
_viewport->fullHeightValue(
|
rpl::combine(
|
||||||
) | rpl::start_with_next([=](int height) {
|
_layout->widthValue(),
|
||||||
_videoWrap->resize(_videoWrap->width(), height);
|
_call->hasNotShownVideoValue()
|
||||||
move();
|
) | rpl::start_with_next([=](int width, bool has) {
|
||||||
resize();
|
const auto height = has ? st::groupCallVideoPlaceholderHeight : 0;
|
||||||
|
_videoPlaceholder->setGeometry(0, 0, width, height);
|
||||||
|
}, _videoPlaceholder->lifetime());
|
||||||
|
|
||||||
|
SetupVideoPlaceholder(_videoPlaceholder.get(), _call->peer());
|
||||||
|
|
||||||
|
rpl::combine(
|
||||||
|
_videoPlaceholder->heightValue(),
|
||||||
|
_viewport->fullHeightValue()
|
||||||
|
) | rpl::start_with_next([=](int placeholder, int viewport) {
|
||||||
|
_videoWrap->resize(
|
||||||
|
_videoWrap->width(),
|
||||||
|
std::max(placeholder, viewport));
|
||||||
|
if (viewport > 0) {
|
||||||
|
move();
|
||||||
|
resize();
|
||||||
|
}
|
||||||
}, _viewport->lifetime());
|
}, _viewport->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,7 @@ private:
|
||||||
std::unique_ptr<Controller> _listController;
|
std::unique_ptr<Controller> _listController;
|
||||||
not_null<Ui::VerticalLayout*> _layout;
|
not_null<Ui::VerticalLayout*> _layout;
|
||||||
const not_null<Ui::RpWidget*> _videoWrap;
|
const not_null<Ui::RpWidget*> _videoWrap;
|
||||||
|
const std::unique_ptr<Ui::RpWidget> _videoPlaceholder;
|
||||||
std::unique_ptr<Viewport> _viewport;
|
std::unique_ptr<Viewport> _viewport;
|
||||||
rpl::variable<Ui::RpWidget*> _addMemberButton = nullptr;
|
rpl::variable<Ui::RpWidget*> _addMemberButton = nullptr;
|
||||||
RpWidget *_topSkip = nullptr;
|
RpWidget *_topSkip = nullptr;
|
||||||
|
|
|
@ -854,6 +854,7 @@ void Panel::setupVideo(not_null<Viewport*> viewport) {
|
||||||
viewport->add(
|
viewport->add(
|
||||||
endpoint,
|
endpoint,
|
||||||
VideoTileTrack{ track.track.get(), row },
|
VideoTileTrack{ track.track.get(), row },
|
||||||
|
track.trackSize.value(),
|
||||||
std::move(pinned));
|
std::move(pinned));
|
||||||
};
|
};
|
||||||
for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
|
for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
|
||||||
|
|
|
@ -225,10 +225,12 @@ void Viewport::setControlsShown(float64 shown) {
|
||||||
void Viewport::add(
|
void Viewport::add(
|
||||||
const VideoEndpoint &endpoint,
|
const VideoEndpoint &endpoint,
|
||||||
VideoTileTrack track,
|
VideoTileTrack track,
|
||||||
|
rpl::producer<QSize> trackSize,
|
||||||
rpl::producer<bool> pinned) {
|
rpl::producer<bool> pinned) {
|
||||||
_tiles.push_back(std::make_unique<VideoTile>(
|
_tiles.push_back(std::make_unique<VideoTile>(
|
||||||
endpoint,
|
endpoint,
|
||||||
track,
|
track,
|
||||||
|
std::move(trackSize),
|
||||||
std::move(pinned),
|
std::move(pinned),
|
||||||
[=] { widget()->update(); }));
|
[=] { widget()->update(); }));
|
||||||
|
|
||||||
|
@ -711,11 +713,13 @@ void Viewport::updateTilesGeometryColumn(int outerWidth) {
|
||||||
const auto layoutNext = [&](not_null<VideoTile*> tile) {
|
const auto layoutNext = [&](not_null<VideoTile*> tile) {
|
||||||
const auto size = tile->trackOrUserpicSize();
|
const auto size = tile->trackOrUserpicSize();
|
||||||
const auto shown = !size.isEmpty() && _large && tile != _large;
|
const auto shown = !size.isEmpty() && _large && tile != _large;
|
||||||
const auto height = shown
|
const auto height = st::groupCallNarrowVideoHeight;
|
||||||
? st::groupCallNarrowVideoHeight
|
if (!shown) {
|
||||||
: 0;
|
tile->hide();
|
||||||
setTileGeometry(tile, { 0, y + top, outerWidth, height });
|
} else {
|
||||||
top += height ? (height + st::groupCallVideoSmallSkip) : 0;
|
setTileGeometry(tile, { 0, y + top, outerWidth, height });
|
||||||
|
top += height + st::groupCallVideoSmallSkip;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const auto topPeer = _large ? _large->row()->peer().get() : nullptr;
|
const auto topPeer = _large ? _large->row()->peer().get() : nullptr;
|
||||||
const auto reorderNeeded = [&] {
|
const auto reorderNeeded = [&] {
|
||||||
|
|
|
@ -39,6 +39,7 @@ enum class VideoQuality;
|
||||||
struct VideoTileTrack {
|
struct VideoTileTrack {
|
||||||
Webrtc::VideoTrack *track = nullptr;
|
Webrtc::VideoTrack *track = nullptr;
|
||||||
MembersRow *row = nullptr;
|
MembersRow *row = nullptr;
|
||||||
|
rpl::variable<QSize> trackSize;
|
||||||
|
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return track != nullptr;
|
return track != nullptr;
|
||||||
|
@ -77,6 +78,7 @@ public:
|
||||||
void add(
|
void add(
|
||||||
const VideoEndpoint &endpoint,
|
const VideoEndpoint &endpoint,
|
||||||
VideoTileTrack track,
|
VideoTileTrack track,
|
||||||
|
rpl::producer<QSize> trackSize,
|
||||||
rpl::producer<bool> pinned);
|
rpl::producer<bool> pinned);
|
||||||
void remove(const VideoEndpoint &endpoint);
|
void remove(const VideoEndpoint &endpoint);
|
||||||
void showLarge(const VideoEndpoint &endpoint);
|
void showLarge(const VideoEndpoint &endpoint);
|
||||||
|
@ -91,6 +93,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] rpl::lifetime &lifetime();
|
[[nodiscard]] rpl::lifetime &lifetime();
|
||||||
|
|
||||||
|
static constexpr auto kShadowMaxAlpha = 80;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Textures;
|
struct Textures;
|
||||||
class VideoTile;
|
class VideoTile;
|
||||||
|
@ -132,8 +136,6 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr auto kShadowMaxAlpha = 80;
|
|
||||||
|
|
||||||
void setup();
|
void setup();
|
||||||
[[nodiscard]] bool wide() const;
|
[[nodiscard]] bool wide() const;
|
||||||
|
|
||||||
|
|
|
@ -320,7 +320,6 @@ Viewport::RendererGL::RendererGL(not_null<Viewport*> owner)
|
||||||
void Viewport::RendererGL::init(
|
void Viewport::RendererGL::init(
|
||||||
not_null<QOpenGLWidget*> widget,
|
not_null<QOpenGLWidget*> widget,
|
||||||
QOpenGLFunctions &f) {
|
QOpenGLFunctions &f) {
|
||||||
_factor = widget->devicePixelRatio();
|
|
||||||
_frameBuffer.emplace();
|
_frameBuffer.emplace();
|
||||||
_frameBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw);
|
_frameBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw);
|
||||||
_frameBuffer->create();
|
_frameBuffer->create();
|
||||||
|
@ -427,7 +426,11 @@ void Viewport::RendererGL::setDefaultViewport(QOpenGLFunctions &f) {
|
||||||
void Viewport::RendererGL::paint(
|
void Viewport::RendererGL::paint(
|
||||||
not_null<QOpenGLWidget*> widget,
|
not_null<QOpenGLWidget*> widget,
|
||||||
QOpenGLFunctions &f) {
|
QOpenGLFunctions &f) {
|
||||||
_factor = widget->devicePixelRatio();
|
const auto factor = widget->devicePixelRatio();
|
||||||
|
if (_factor != factor) {
|
||||||
|
_factor = factor;
|
||||||
|
_buttons.invalidate();
|
||||||
|
}
|
||||||
_viewport = widget->size();
|
_viewport = widget->size();
|
||||||
|
|
||||||
const auto defaultFramebufferObject = widget->defaultFramebufferObject();
|
const auto defaultFramebufferObject = widget->defaultFramebufferObject();
|
||||||
|
@ -1054,7 +1057,6 @@ void Viewport::RendererGL::ensureButtonsImage() {
|
||||||
if (_buttons) {
|
if (_buttons) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto factor = cIntRetinaFactor();
|
|
||||||
const auto pinOnSize = VideoTile::PinInnerSize(true);
|
const auto pinOnSize = VideoTile::PinInnerSize(true);
|
||||||
const auto pinOffSize = VideoTile::PinInnerSize(false);
|
const auto pinOffSize = VideoTile::PinInnerSize(false);
|
||||||
const auto backSize = VideoTile::BackInnerSize();
|
const auto backSize = VideoTile::BackInnerSize();
|
||||||
|
@ -1074,18 +1076,18 @@ void Viewport::RendererGL::ensureButtonsImage() {
|
||||||
+ backSize.height()
|
+ backSize.height()
|
||||||
+ muteSize.height()
|
+ muteSize.height()
|
||||||
+ pausedSize.height()));
|
+ pausedSize.height()));
|
||||||
const auto imageSize = fullSize * factor;
|
const auto imageSize = fullSize * _factor;
|
||||||
auto image = _buttons.takeImage();
|
auto image = _buttons.takeImage();
|
||||||
if (image.size() != imageSize) {
|
if (image.size() != imageSize) {
|
||||||
image = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
|
image = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
|
||||||
}
|
}
|
||||||
image.fill(Qt::transparent);
|
image.fill(Qt::transparent);
|
||||||
image.setDevicePixelRatio(cRetinaFactor());
|
image.setDevicePixelRatio(_factor);
|
||||||
{
|
{
|
||||||
auto p = Painter(&image);
|
auto p = Painter(&image);
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
|
||||||
_pinOn = QRect(QPoint(), pinOnSize * factor);
|
_pinOn = QRect(QPoint(), pinOnSize * _factor);
|
||||||
VideoTile::PaintPinButton(
|
VideoTile::PaintPinButton(
|
||||||
p,
|
p,
|
||||||
true,
|
true,
|
||||||
|
@ -1096,7 +1098,9 @@ void Viewport::RendererGL::ensureButtonsImage() {
|
||||||
&_pinIcon);
|
&_pinIcon);
|
||||||
|
|
||||||
const auto pinOffTop = pinOnSize.height();
|
const auto pinOffTop = pinOnSize.height();
|
||||||
_pinOff = QRect(QPoint(0, pinOffTop) * factor, pinOffSize * factor);
|
_pinOff = QRect(
|
||||||
|
QPoint(0, pinOffTop) * _factor,
|
||||||
|
pinOffSize * _factor);
|
||||||
VideoTile::PaintPinButton(
|
VideoTile::PaintPinButton(
|
||||||
p,
|
p,
|
||||||
false,
|
false,
|
||||||
|
@ -1107,7 +1111,7 @@ void Viewport::RendererGL::ensureButtonsImage() {
|
||||||
&_pinIcon);
|
&_pinIcon);
|
||||||
|
|
||||||
const auto backTop = pinOffTop + pinOffSize.height();
|
const auto backTop = pinOffTop + pinOffSize.height();
|
||||||
_back = QRect(QPoint(0, backTop) * factor, backSize * factor);
|
_back = QRect(QPoint(0, backTop) * _factor, backSize * _factor);
|
||||||
VideoTile::PaintBackButton(
|
VideoTile::PaintBackButton(
|
||||||
p,
|
p,
|
||||||
0,
|
0,
|
||||||
|
@ -1116,16 +1120,18 @@ void Viewport::RendererGL::ensureButtonsImage() {
|
||||||
&_pinBackground);
|
&_pinBackground);
|
||||||
|
|
||||||
const auto muteTop = backTop + backSize.height();
|
const auto muteTop = backTop + backSize.height();
|
||||||
_muteOn = QRect(QPoint(0, muteTop) * factor, muteSize * factor);
|
_muteOn = QRect(QPoint(0, muteTop) * _factor, muteSize * _factor);
|
||||||
_muteIcon.paint(p, { 0, muteTop }, 1.);
|
_muteIcon.paint(p, { 0, muteTop }, 1.);
|
||||||
|
|
||||||
_muteOff = QRect(
|
_muteOff = QRect(
|
||||||
QPoint(muteSize.width(), muteTop) * factor,
|
QPoint(muteSize.width(), muteTop) * _factor,
|
||||||
muteSize * factor);
|
muteSize * _factor);
|
||||||
_muteIcon.paint(p, { muteSize.width(), muteTop }, 0.);
|
_muteIcon.paint(p, { muteSize.width(), muteTop }, 0.);
|
||||||
|
|
||||||
const auto pausedTop = muteTop + muteSize.height();
|
const auto pausedTop = muteTop + muteSize.height();
|
||||||
_paused = QRect(QPoint(0, pausedTop) * factor, pausedSize * factor);
|
_paused = QRect(
|
||||||
|
QPoint(0, pausedTop) * _factor,
|
||||||
|
pausedSize * _factor);
|
||||||
st::groupCallPaused.paint(p, 0, pausedTop, fullSize.width());
|
st::groupCallPaused.paint(p, 0, pausedTop, fullSize.width());
|
||||||
}
|
}
|
||||||
_buttons.setImage(std::move(image));
|
_buttons.setImage(std::move(image));
|
||||||
|
|
|
@ -25,11 +25,13 @@ constexpr auto kPausedVideoSize = 90;
|
||||||
Viewport::VideoTile::VideoTile(
|
Viewport::VideoTile::VideoTile(
|
||||||
const VideoEndpoint &endpoint,
|
const VideoEndpoint &endpoint,
|
||||||
VideoTileTrack track,
|
VideoTileTrack track,
|
||||||
|
rpl::producer<QSize> trackSize,
|
||||||
rpl::producer<bool> pinned,
|
rpl::producer<bool> pinned,
|
||||||
Fn<void()> update)
|
Fn<void()> update)
|
||||||
: _endpoint(endpoint)
|
: _endpoint(endpoint)
|
||||||
, _update(std::move(update))
|
, _update(std::move(update))
|
||||||
, _track(track) {
|
, _track(track)
|
||||||
|
, _trackSize(std::move(trackSize)) {
|
||||||
Expects(track.track != nullptr);
|
Expects(track.track != nullptr);
|
||||||
Expects(track.row != nullptr);
|
Expects(track.row != nullptr);
|
||||||
|
|
||||||
|
@ -254,19 +256,7 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
_track.track->renderNextFrame(
|
_track.track->renderNextFrame(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next(_update, _lifetime);
|
||||||
const auto size = _track.track->frameSize();
|
|
||||||
if (size.isEmpty()) {
|
|
||||||
_track.track->markFrameShown();
|
|
||||||
} else {
|
|
||||||
_trackSize = size;
|
|
||||||
}
|
|
||||||
_update();
|
|
||||||
}, _lifetime);
|
|
||||||
|
|
||||||
if (const auto size = _track.track->frameSize(); !size.isEmpty()) {
|
|
||||||
_trackSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTopControlsSize();
|
updateTopControlsSize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
VideoTile(
|
VideoTile(
|
||||||
const VideoEndpoint &endpoint,
|
const VideoEndpoint &endpoint,
|
||||||
VideoTileTrack track,
|
VideoTileTrack track,
|
||||||
|
rpl::producer<QSize> trackSize,
|
||||||
rpl::producer<bool> pinned,
|
rpl::producer<bool> pinned,
|
||||||
Fn<void()> update);
|
Fn<void()> update);
|
||||||
|
|
||||||
|
|
|
@ -304,10 +304,10 @@ void Application::showOpenGLCrashNotification() {
|
||||||
Local::writeSettings();
|
Local::writeSettings();
|
||||||
};
|
};
|
||||||
_window->show(Box<ConfirmBox>(
|
_window->show(Box<ConfirmBox>(
|
||||||
"Last time OpenGL crashed on initialization. "
|
"There may be a problem with your graphics drivers and OpenGL. "
|
||||||
"Perhaps it is a problem with your graphics card driver.\n\n"
|
"Try updating your drivers.\n\n"
|
||||||
"Right now OpenGL was disabled. You can try to enable it back "
|
"OpenGL has been disabled. You can try to enable it again "
|
||||||
"or keep it disabled, if it continues crashing.",
|
"or keep it disabled if crashes continue.",
|
||||||
"Enable",
|
"Enable",
|
||||||
"Keep Disabled",
|
"Keep Disabled",
|
||||||
enable,
|
enable,
|
||||||
|
|
|
@ -3274,7 +3274,6 @@ void OverlayWidget::paint(not_null<Renderer*> renderer) {
|
||||||
}
|
}
|
||||||
paintRadialLoading(renderer);
|
paintRadialLoading(renderer);
|
||||||
} else {
|
} else {
|
||||||
int a = 0;
|
|
||||||
if (_themePreviewShown) {
|
if (_themePreviewShown) {
|
||||||
renderer->paintThemePreview(_themePreviewRect);
|
renderer->paintThemePreview(_themePreviewRect);
|
||||||
} else if (documentBubbleShown() && !_docRect.isEmpty()) {
|
} else if (documentBubbleShown() && !_docRect.isEmpty()) {
|
||||||
|
|
|
@ -406,21 +406,22 @@ void Pip::RendererGL::paintTransformedContent(
|
||||||
_f->glActiveTexture(rgbaFrame ? GL_TEXTURE1 : GL_TEXTURE3);
|
_f->glActiveTexture(rgbaFrame ? GL_TEXTURE1 : GL_TEXTURE3);
|
||||||
_shadowImage.bind(*_f);
|
_shadowImage.bind(*_f);
|
||||||
|
|
||||||
|
const auto globalFactor = cIntRetinaFactor();
|
||||||
const auto fadeAlpha = st::radialBg->c.alphaF() * geometry.fade;
|
const auto fadeAlpha = st::radialBg->c.alphaF() * geometry.fade;
|
||||||
const auto roundRect = transformRect(RoundingRect(geometry));
|
const auto roundRect = transformRect(RoundingRect(geometry));
|
||||||
program->setUniformValue("roundRect", Uniform(roundRect));
|
program->setUniformValue("roundRect", Uniform(roundRect));
|
||||||
program->setUniformValue("h_texture", GLint(rgbaFrame ? 1 : 3));
|
program->setUniformValue("h_texture", GLint(rgbaFrame ? 1 : 3));
|
||||||
program->setUniformValue("h_size", QSizeF(_shadowImage.image().size()));
|
program->setUniformValue("h_size", QSizeF(_shadowImage.image().size()));
|
||||||
program->setUniformValue("h_extend", QVector4D(
|
program->setUniformValue("h_extend", QVector4D(
|
||||||
st::callShadow.extend.left(),
|
st::callShadow.extend.left() * globalFactor,
|
||||||
st::callShadow.extend.top(),
|
st::callShadow.extend.top() * globalFactor,
|
||||||
st::callShadow.extend.right(),
|
st::callShadow.extend.right() * globalFactor,
|
||||||
st::callShadow.extend.bottom()));
|
st::callShadow.extend.bottom() * globalFactor));
|
||||||
program->setUniformValue("h_components", QVector4D(
|
program->setUniformValue("h_components", QVector4D(
|
||||||
float(st::callShadow.topLeft.width()),
|
float(st::callShadow.topLeft.width() * globalFactor),
|
||||||
float(st::callShadow.topLeft.height()),
|
float(st::callShadow.topLeft.height() * globalFactor),
|
||||||
float(st::callShadow.left.width()),
|
float(st::callShadow.left.width() * globalFactor),
|
||||||
float(st::callShadow.top.height())));
|
float(st::callShadow.top.height() * globalFactor)));
|
||||||
program->setUniformValue(
|
program->setUniformValue(
|
||||||
"roundRadius",
|
"roundRadius",
|
||||||
GLfloat(st::roundRadiusLarge * _factor));
|
GLfloat(st::roundRadiusLarge * _factor));
|
||||||
|
|
2
Telegram/ThirdParty/tgcalls
vendored
2
Telegram/ThirdParty/tgcalls
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 9adb220f798cadc0848d3e1efbf32fb2a98a3bcb
|
Subproject commit d147f286cfe2a23c49c438a63be9aa1c0a04344b
|
Loading…
Add table
Reference in a new issue