diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index ebe8d608d..5cdaf5652 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -49,6 +49,8 @@ constexpr auto kUpdateSendActionEach = crl::time(500); constexpr auto kPlayConnectingEach = crl::time(1056) + 2 * crl::time(1000); constexpr auto kFixManualLargeVideoDuration = 5 * crl::time(1000); constexpr auto kFixSpeakingLargeVideoDuration = 3 * crl::time(1000); +constexpr auto kFullAsMediumsCount = 4; // 1 Full is like 4 Mediums. +constexpr auto kMaxMediumQualities = 16; // 4 Fulls or 16 Mediums. [[nodiscard]] std::unique_ptr CreateMediaDevices() { const auto &settings = Core::App().settings(); @@ -2435,6 +2437,9 @@ void GroupCall::updateRequestedVideoChannels() { channels.reserve(_activeVideoTracks.size()); const auto &camera = cameraSharingEndpoint(); const auto &screen = screenSharingEndpoint(); + auto mediums = 0; + auto fullcameras = 0; + auto fullscreencasts = 0; for (const auto &[endpoint, video] : _activeVideoTracks) { const auto &endpointId = endpoint.id; if (endpointId == camera || endpointId == screen) { @@ -2447,24 +2452,74 @@ void GroupCall::updateRequestedVideoChannels() { if (!params) { continue; } + const auto min = (video->quality == Group::VideoQuality::Full + && endpoint.type == VideoEndpointType::Screen) + ? Quality::Full + : Quality::Thumbnail; + const auto max = (video->quality == Group::VideoQuality::Full) + ? Quality::Full + : (video->quality == Group::VideoQuality::Medium + && endpoint.type != VideoEndpointType::Screen) + ? Quality::Medium + : Quality::Thumbnail; + if (max == Quality::Full) { + if (endpoint.type == VideoEndpointType::Screen) { + ++fullscreencasts; + } else { + ++fullcameras; + } + } else if (max == Quality::Medium) { + ++mediums; + } channels.push_back({ .audioSsrc = participant->ssrc, .endpointId = endpointId, .ssrcGroups = (params->camera.endpointId == endpointId ? params->camera.ssrcGroups : params->screen.ssrcGroups), - .minQuality = ((video->quality == Group::VideoQuality::Full - && endpoint.type == VideoEndpointType::Screen) - ? Quality::Full - : Quality::Thumbnail), - .maxQuality = ((video->quality == Group::VideoQuality::Full) - ? Quality::Full - : (video->quality == Group::VideoQuality::Medium - && endpoint.type != VideoEndpointType::Screen) - ? Quality::Medium - : Quality::Thumbnail), + .minQuality = min, + .maxQuality = max, }); } + + // We limit `count(Full) * kFullAsMediumsCount + count(medium)`. + // + // Try to preserve all qualities; If not + // Try to preserve all screencasts as Full and cameras as Medium; If not + // Try to preserve all screencasts as Full; If not + // Try to preserve all cameras as Medium; + const auto mediumsCount = mediums + + (fullcameras + fullscreencasts) * kFullAsMediumsCount; + const auto downgradeSome = (mediumsCount > kMaxMediumQualities); + const auto downgradeAll = (fullscreencasts * kFullAsMediumsCount) + > kMaxMediumQualities; + if (downgradeSome) { + for (auto &channel : channels) { + if (channel.maxQuality == Quality::Full) { + const auto camera = (channel.minQuality != Quality::Full); + if (camera) { + channel.maxQuality = Quality::Medium; + } else if (downgradeAll) { + channel.maxQuality + = channel.minQuality + = Quality::Thumbnail; + --fullscreencasts; + } + } + } + mediums += fullcameras; + fullcameras = 0; + if (downgradeAll) { + fullscreencasts = 0; + } + } + if (mediums > kMaxMediumQualities) { + for (auto &channel : channels) { + if (channel.maxQuality == Quality::Medium) { + channel.maxQuality = Quality::Thumbnail; + } + } + } _instance->setRequestedVideoChannels(std::move(channels)); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp index 07ac9aa28..756ebdb4d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport.cpp @@ -191,7 +191,7 @@ void Viewport::updateSelected(QPoint position) { return; } for (const auto &tile : _tiles) { - const auto geometry = tile->shown() + const auto geometry = tile->visible() ? tile->geometry() : QRect(); if (geometry.contains(position)) { @@ -763,7 +763,11 @@ void Viewport::setTileGeometry(not_null tile, QRect geometry) { const auto kMedium = style::ConvertScale(540); const auto kSmall = style::ConvertScale(240); const auto &endpoint = tile->endpoint(); - const auto quality = (min >= kMedium) + const auto forceThumbnailQuality = !wide() + && (ranges::count(_tiles, false, &VideoTile::hidden) > 1); + const auto quality = forceThumbnailQuality + ? VideoQuality::Thumbnail + : (min >= kMedium) ? VideoQuality::Full : (min >= kSmall) ? VideoQuality::Medium diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp index 42b7fc8f9..f2cf335ba 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_opengl.cpp @@ -439,7 +439,7 @@ void Viewport::RendererGL::paint( validateDatas(); auto index = 0; for (const auto &tile : _owner->_tiles) { - if (!tile->shown()) { + if (!tile->visible()) { index++; continue; } diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp index 66b549809..b76bf0bcf 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_raster.cpp @@ -45,7 +45,7 @@ void Viewport::RendererSW::paintFallback( tileData.stale = true; } for (const auto &tile : _owner->_tiles) { - if (!tile->shown()) { + if (!tile->visible()) { continue; } paintTile(p, tile.get(), bounding, bg); diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.cpp b/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.cpp index ccb5d33bc..5a438c14f 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.cpp @@ -82,14 +82,14 @@ bool Viewport::VideoTile::screencast() const { void Viewport::VideoTile::setGeometry( QRect geometry, TileAnimation animation) { - _shown = true; + _hidden = false; _geometry = geometry; _animation = animation; updateTopControlsPosition(); } void Viewport::VideoTile::hide() { - _shown = false; + _hidden = true; _quality = std::nullopt; } @@ -106,7 +106,7 @@ void Viewport::VideoTile::toggleTopControlsShown(bool shown) { } bool Viewport::VideoTile::updateRequestedQuality(VideoQuality quality) { - if (!_shown) { + if (_hidden) { _quality = std::nullopt; return false; } else if (_quality && *_quality == quality) { @@ -249,7 +249,7 @@ void Viewport::VideoTile::setup(rpl::producer pinned) { }) | rpl::start_with_next([=](bool pinned) { _pinned = pinned; updateTopControlsSize(); - if (_shown) { + if (!_hidden) { updateTopControlsPosition(); _update(); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.h b/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.h index 5b05f6797..3a2efa267 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.h +++ b/Telegram/SourceFiles/calls/group/calls_group_viewport_tile.h @@ -45,8 +45,11 @@ public: [[nodiscard]] bool pinned() const { return _pinned; } - [[nodiscard]] bool shown() const { - return _shown && !_geometry.isEmpty(); + [[nodiscard]] bool hidden() const { + return _hidden; + } + [[nodiscard]] bool visible() const { + return !_hidden && !_geometry.isEmpty(); } [[nodiscard]] QRect pinOuter() const; [[nodiscard]] QRect pinInner() const; @@ -115,7 +118,7 @@ private: Ui::Animations::Simple _topControlsShownAnimation; bool _topControlsShown = false; bool _pinned = false; - bool _shown = false; + bool _hidden = true; std::optional _quality; rpl::lifetime _lifetime;