From e12fe974b2b4266618ba3c60f33d41a113192cb7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 23 Apr 2021 19:05:52 +0400 Subject: [PATCH] Add pinned video in wide mode. --- .../calls/group/calls_group_call.cpp | 13 ++- .../calls/group/calls_group_panel.cpp | 105 +++++++++++++++++- .../calls/group/calls_group_panel.h | 4 + 3 files changed, 113 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 1910027c38..878b9d335e 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -410,7 +410,9 @@ void GroupCall::switchToCamera() { return; } _videoDeviceId = _videoInputId; - if (_videoOutgoing) + if (_videoOutgoing->state() != Webrtc::VideoState::Active) { + _videoOutgoing->setState(Webrtc::VideoState::Active); + } _videoCapture->switchToDevice(_videoDeviceId.toStdString()); } @@ -419,6 +421,9 @@ void GroupCall::switchToScreenSharing(const QString &uniqueId) { return; } _videoDeviceId = uniqueId; + if (_videoOutgoing->state() != Webrtc::VideoState::Active) { + _videoOutgoing->setState(Webrtc::VideoState::Active); + } _videoCapture->switchToDevice(_videoDeviceId.toStdString()); } @@ -1432,15 +1437,15 @@ void GroupCall::setupOutgoingVideo() { } _videoOutgoing->stateValue( ) | rpl::start_with_next([=](Webrtc::VideoState state) { - if (state != Webrtc::VideoState::Inactive && !hasDevices()) { + //if (state != Webrtc::VideoState::Inactive && !hasDevices()) { //_errors.fire({ ErrorType::NoCamera }); // #TODO videochats - _videoOutgoing->setState(Webrtc::VideoState::Inactive); + //_videoOutgoing->setState(Webrtc::VideoState::Inactive); //} else if (state != Webrtc::VideoState::Inactive // && _instance // && !_instance->supportsVideo()) { // _errors.fire({ ErrorType::NotVideoCall }); // _videoOutgoing->setState(Webrtc::VideoState::Inactive); - } else if (state != Webrtc::VideoState::Inactive) { + /*} else */if (state != Webrtc::VideoState::Inactive) { // Paused not supported right now. Assert(state == Webrtc::VideoState::Active); if (!_videoCapture) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 4a1dfcb0d8..103ddf7cac 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -47,6 +47,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer_rpl.h" #include "app.h" #include "apiwrap.h" // api().kickParticipant. +#include "media/view/media_view_pip.h" +#include "webrtc/webrtc_video_track.h" #include "styles/style_calls.h" #include "styles/style_layers.h" @@ -904,6 +906,91 @@ void Panel::setupMembers() { addMembers(); } }, _callLifetime); + + setupPinnedVideo(); +} + +void Panel::setupPinnedVideo() { + _pinnedVideo.create(widget()); + _pinnedVideo->setVisible(_mode == PanelMode::Wide); + + rpl::combine( + _pinnedVideo->shownValue(), + _call->videoLargeTrackValue() + ) | rpl::map([](bool shown, Webrtc::VideoTrack *track) { + return shown ? track : nullptr; + }) | rpl::distinct_until_changed( + ) | rpl::start_with_next([=](Webrtc::VideoTrack *track) { + _pinnedTrackLifetime.destroy(); + if (!track) { + _pinnedVideo->paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + QPainter(_pinnedVideo.data()).fillRect(clip, Qt::black); + }, _pinnedTrackLifetime); + _pinnedVideo->update(); + return; + } + track->renderNextFrame( + ) | rpl::start_with_next([=] { + const auto size = track->frameSize(); + if (size.isEmpty()) { + track->markFrameShown(); + } else { + _pinnedVideo->update(); + } + }, _pinnedTrackLifetime); + + _pinnedVideo->paintRequest( + ) | rpl::start_with_next([=] { + const auto [image, rotation] + = track->frameOriginalWithRotation(); + if (image.isNull()) { + return; + } + auto p = QPainter(_pinnedVideo); + auto hq = PainterHighQualityEnabler(p); + using namespace Media::View; + const auto size = _pinnedVideo->size(); + const auto scaled = FlipSizeByRotation( + image.size(), + rotation + ).scaled(size, Qt::KeepAspectRatio); + const auto left = (size.width() - scaled.width()) / 2; + const auto top = (size.height() - scaled.height()) / 2; + const auto target = QRect(QPoint(left, top), scaled); + if (UsePainterRotation(rotation)) { + if (rotation) { + p.save(); + p.rotate(rotation); + } + p.drawImage(RotatedRect(target, rotation), image); + if (rotation) { + p.restore(); + } + } else if (rotation) { + p.drawImage(target, RotateFrameImage(image, rotation)); + } else { + p.drawImage(target, image); + } + if (left > 0) { + p.fillRect(0, 0, left, size.height(), Qt::black); + } + if (const auto right = left + scaled.width() + ; right < size.width()) { + const auto fill = size.width() - right; + p.fillRect(right, 0, fill, size.height(), Qt::black); + } + if (top > 0) { + p.fillRect(0, 0, size.width(), top, Qt::black); + } + if (const auto bottom = top + scaled.height() + ; bottom < size.height()) { + const auto fill = size.height() - bottom; + p.fillRect(0, bottom, size.width(), fill, Qt::black); + } + track->markFrameShown(); + }, _pinnedTrackLifetime); + }, widget()->lifetime()); } void Panel::setupJoinAsChangedToasts() { @@ -1402,6 +1489,7 @@ bool Panel::updateMode() { if (_members) { _members->setMode(mode); } + _pinnedVideo->setVisible(mode == PanelMode::Wide); updateControlsGeometry(); return true; } @@ -1457,18 +1545,25 @@ void Panel::updateMembersGeometry() { return; } const auto desiredHeight = _members->desiredHeight(); + const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; + const auto membersTop = st::groupCallMembersTop; + const auto availableHeight = muteTop + - membersTop + - st::groupCallMembersMargin.bottom(); if (_mode == PanelMode::Wide) { _members->setGeometry( st::groupCallNarrowSkip, 0, st::groupCallNarrowSize.width(), std::min(desiredHeight, widget()->height())); + const auto pinnedLeft = st::groupCallNarrowSkip * 2 + + st::groupCallNarrowSize.width(); + _pinnedVideo->setGeometry( + pinnedLeft, + membersTop, + widget()->width() - pinnedLeft, + availableHeight); } else { - const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; - const auto membersTop = st::groupCallMembersTop; - const auto availableHeight = muteTop - - membersTop - - st::groupCallMembersMargin.bottom(); const auto membersWidthAvailable = widget()->width() - st::groupCallMembersMargin.left() - st::groupCallMembersMargin.right(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.h b/Telegram/SourceFiles/calls/group/calls_group_panel.h index 51e23a5f7e..8b6201ab49 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.h @@ -31,6 +31,7 @@ class CallButton; class CallMuteButton; class IconButton; class FlatLabel; +class RpWidget; template class FadeWrap; template @@ -81,6 +82,7 @@ private: void initGeometry(); void setupScheduledLabels(rpl::producer date); void setupMembers(); + void setupPinnedVideo(); void setupJoinAsChangedToasts(); void setupTitleChangedToasts(); void setupAllowedToSpeakToasts(); @@ -135,6 +137,8 @@ private: object_ptr _menu = { nullptr }; object_ptr _joinAsToggle = { nullptr }; object_ptr _members = { nullptr }; + object_ptr _pinnedVideo = { nullptr }; + rpl::lifetime _pinnedTrackLifetime; object_ptr _startsIn = { nullptr }; object_ptr _countdown = { nullptr }; std::shared_ptr _countdownData;