From 5c5414b680302bef5b9d62644ba8ffdee116053c Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 3 Jul 2020 22:57:30 +0400 Subject: [PATCH] Improve video userpics in chat history. --- .../SourceFiles/data/data_auto_download.cpp | 12 ++ .../SourceFiles/data/data_auto_download.h | 4 + Telegram/SourceFiles/data/data_session.cpp | 4 + .../history/view/media/history_view_photo.cpp | 165 +++++++++++++----- .../history/view/media/history_view_photo.h | 19 +- Telegram/SourceFiles/ui/special_buttons.cpp | 1 + 6 files changed, 157 insertions(+), 48 deletions(-) diff --git a/Telegram/SourceFiles/data/data_auto_download.cpp b/Telegram/SourceFiles/data/data_auto_download.cpp index db9d863c97..a18734111f 100644 --- a/Telegram/SourceFiles/data/data_auto_download.cpp +++ b/Telegram/SourceFiles/data/data_auto_download.cpp @@ -309,6 +309,18 @@ bool ShouldAutoPlay( document->size); } +bool ShouldAutoPlay( + const Full &data, + not_null peer, + not_null photo) { + const auto source = SourceFromPeer(peer); + const auto size = photo->videoByteSize(); + return photo->hasVideo() + && (data.shouldDownload(source, Type::AutoPlayGIF, size) + || data.shouldDownload(source, Type::AutoPlayVideo, size) + || data.shouldDownload(source, Type::AutoPlayVideoMessage, size)); +} + Full WithDisabledAutoPlay(const Full &data) { auto result = data; for (const auto source : enums_view(kSourcesCount)) { diff --git a/Telegram/SourceFiles/data/data_auto_download.h b/Telegram/SourceFiles/data/data_auto_download.h index 37c21a6201..410451317f 100644 --- a/Telegram/SourceFiles/data/data_auto_download.h +++ b/Telegram/SourceFiles/data/data_auto_download.h @@ -120,6 +120,10 @@ private: const Full &data, not_null peer, not_null document); +[[nodiscard]] bool ShouldAutoPlay( + const Full &data, + not_null peer, + not_null photo); [[nodiscard]] Full WithDisabledAutoPlay(const Full &data); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 015ae44bde..5e9f9a20c3 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -3208,6 +3208,10 @@ void Session::checkPlayingAnimations() { if (document->isAnimation() || document->isVideoFile()) { check.emplace(view); } + } else if (const auto photo = media->getPhoto()) { + if (photo->hasVideo()) { + check.emplace(view); + } } } } diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index a758792abe..f825c91e0c 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_document.h" #include "main/main_session.h" +#include "main/main_session_settings.h" #include "ui/image/image.h" #include "ui/grouped_layout.h" #include "data/data_session.h" @@ -25,6 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo.h" #include "data/data_photo_media.h" #include "data/data_file_origin.h" +#include "data/data_auto_download.h" +#include "core/application.h" #include "app.h" #include "styles/style_history.h" @@ -35,6 +38,17 @@ using Data::PhotoSize; } // namespace +struct Photo::Streamed { + explicit Streamed(std::shared_ptr<::Media::Streaming::Document> shared); + ::Media::Streaming::Instance instance; + QImage frozenFrame; +}; + +Photo::Streamed::Streamed( + std::shared_ptr<::Media::Streaming::Document> shared) +: instance(std::move(shared), nullptr) { +} + Photo::Photo( not_null parent, not_null realParent, @@ -61,7 +75,7 @@ Photo::~Photo() { if (_streamed || _dataMedia) { if (_streamed) { _data->owner().streaming().keepAlive(_data); - setStreamed(nullptr); + stopAnimation(); } if (_dataMedia) { _data->owner().keepAlive(base::take(_dataMedia)); @@ -107,7 +121,7 @@ bool Photo::hasHeavyPart() const { } void Photo::unloadHeavyPart() { - setStreamed(nullptr); + stopAnimation(); _dataMedia = nullptr; } @@ -220,7 +234,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time auto rthumb = style::rtlrect(paintx, painty, paintw, painth, width()); if (_serviceWidth > 0) { - paintUserpicFrame(p, rthumb.topLeft()); + paintUserpicFrame(p, rthumb.topLeft(), selected); } else { if (bubble) { if (!_caption.isEmpty()) { @@ -316,21 +330,40 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time } } -void Photo::paintUserpicFrame(Painter &p, QPoint photoPosition) const { - const_cast(this)->validateVideo(); +void Photo::paintUserpicFrame( + Painter &p, + QPoint photoPosition, + bool selected) const { + const auto autoplay = _data->videoCanBePlayed() && videoAutoplayEnabled(); + const auto startPlay = autoplay && !_streamed; + if (startPlay) { + const_cast(this)->playAnimation(true); + } else { + checkStreamedIsStarted(); + } + + const auto size = QSize{ _pixw, _pixh }; + const auto rect = QRect(photoPosition, size); if (_streamed - && _streamed->player().ready() - && !_streamed->player().videoSize().isEmpty()) { + && _streamed->instance.player().ready() + && !_streamed->instance.player().videoSize().isEmpty()) { const auto paused = _parent->delegate()->elementIsGifPaused(); auto request = ::Media::Streaming::FrameRequest(); - auto size = QSize{ _pixw, _pixh }; request.outer = size * cIntRetinaFactor(); request.resize = size * cIntRetinaFactor(); request.radius = ImageRoundRadius::Ellipse; - p.drawImage(QRect(photoPosition, size), _streamed->frame(request)); - if (!paused) { - _streamed->markFrameShown(); + if (_streamed->instance.playerLocked()) { + if (_streamed->frozenFrame.isNull()) { + _streamed->frozenFrame = _streamed->instance.frame(request); + } + p.drawImage(rect, _streamed->frozenFrame); + } else { + _streamed->frozenFrame = QImage(); + p.drawImage(rect, _streamed->instance.frame(request)); + if (!paused) { + _streamed->instance.markFrameShown(); + } } return; } @@ -349,7 +382,28 @@ void Photo::paintUserpicFrame(Painter &p, QPoint photoPosition) const { return QPixmap(); } }(); - p.drawPixmap(photoPosition, pix); + p.drawPixmap(rect, pix); + + if (_data->videoCanBePlayed() && !_streamed) { + auto inner = QRect(rect.x() + (rect.width() - st::msgFileSize) / 2, rect.y() + (rect.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); + p.setPen(Qt::NoPen); + if (selected) { + p.setBrush(st::msgDateImgBgSelected); + } else { + const auto over = ClickHandler::showAsActive(_openl); + p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg); + } + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(inner); + } + const auto icon = [&]() -> const style::icon * { + return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay); + }(); + if (icon) { + icon->paintInCenter(p, inner); + } + } } TextState Photo::textState(QPoint point, StateRequest request) const { @@ -615,28 +669,28 @@ void Photo::validateGroupedCache( bool Photo::createStreamingObjects() { using namespace ::Media::Streaming; - setStreamed(std::make_unique( + setStreamed(std::make_unique( history()->owner().streaming().sharedDocument( _data, - _realParent->fullId()), - nullptr)); - _streamed->player().updates( + _realParent->fullId()))); + _streamed->instance.player().updates( ) | rpl::start_with_next_error([=](Update &&update) { handleStreamingUpdate(std::move(update)); }, [=](Error &&error) { handleStreamingError(std::move(error)); - }, _streamed->lifetime()); - if (_streamed->ready()) { - streamingReady(base::duplicate(_streamed->info())); + }, _streamed->instance.lifetime()); + if (_streamed->instance.ready()) { + streamingReady(base::duplicate(_streamed->instance.info())); } - if (!_streamed->valid()) { - setStreamed(nullptr); + if (!_streamed->instance.valid()) { + stopAnimation(); return false; } + checkStreamedIsStarted(); return true; } -void Photo::setStreamed(std::unique_ptr<::Media::Streaming::Instance> value) { +void Photo::setStreamed(std::unique_ptr value) { const auto removed = (_streamed && !value); const auto set = (!_streamed && value); _streamed = std::move(value); @@ -665,14 +719,13 @@ void Photo::handleStreamingUpdate(::Media::Streaming::Update &&update) { void Photo::handleStreamingError(::Media::Streaming::Error &&error) { _data->setVideoPlaybackFailed(); - setStreamed(nullptr); + stopAnimation(); } void Photo::repaintStreamedContent() { - /* const auto own = activeOwnStreamed(); - if (own && !own->frozenFrame.isNull()) { - return; - } else */if (_parent->delegate()->elementIsGifPaused()) { + if (_streamed && !_streamed->frozenFrame.isNull()) { + return; + } else if (_parent->delegate()->elementIsGifPaused()) { return; } history()->owner().requestViewRepaint(_parent); @@ -682,36 +735,62 @@ void Photo::streamingReady(::Media::Streaming::Information &&info) { history()->owner().requestViewRepaint(_parent); } -void Photo::validateVideo() { - if (!_data->videoCanBePlayed()) { - setStreamed(nullptr); - return; - } else if (_streamed) { - return; +void Photo::checkAnimation() { + if (_streamed && !videoAutoplayEnabled()) { + stopAnimation(); } - if (!createStreamingObjects()) { - _data->setVideoPlaybackFailed(); - return; - } - checkStreamedIsStarted(); } -void Photo::checkStreamedIsStarted() { +void Photo::stopAnimation() { + setStreamed(nullptr); +} + +void Photo::playAnimation(bool autoplay) { + ensureDataMediaCreated(); + if (_streamed && autoplay) { + return; + } else if (_streamed && videoAutoplayEnabled()) { + Core::App().showPhoto(_data, _parent->data()); + return; + } + if (_streamed) { + stopAnimation(); + } else if (_data->videoCanBePlayed()) { + if (!videoAutoplayEnabled()) { + history()->owner().checkPlayingAnimations(); + } + if (!createStreamingObjects()) { + _data->setVideoPlaybackFailed(); + return; + } + } +} + +void Photo::checkStreamedIsStarted() const { if (!_streamed) { return; - } else if (_streamed->paused()) { - _streamed->resume(); + } else if (_streamed->instance.paused()) { + _streamed->instance.resume(); } - if (_streamed && !_streamed->active() && !_streamed->failed()) { + if (_streamed + && !_streamed->instance.active() + && !_streamed->instance.failed()) { const auto position = _data->videoStartPosition(); auto options = ::Media::Streaming::PlaybackOptions(); options.position = position; options.mode = ::Media::Streaming::Mode::Video; options.loop = true; - _streamed->play(options); + _streamed->instance.play(options); } } +bool Photo::videoAutoplayEnabled() const { + return Data::AutoDownload::ShouldAutoPlay( + _data->session().settings().autoDownload(), + _realParent->history()->peer, + _data); +} + TextForMimeData Photo::selectedText(TextSelection selection) const { return _caption.toTextForMimeData(selection); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h index 4402f0a003..5c97fd072b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.h +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h @@ -98,8 +98,14 @@ protected: bool dataLoaded() const override; private: + struct Streamed; + void create(FullMsgId contextId, PeerData *chat = nullptr); + void playAnimation(bool autoplay) override; + void stopAnimation() override; + void checkAnimation() override; + void ensureDataMediaCreated() const; void dataMediaCreated() const; @@ -113,15 +119,18 @@ private: not_null cacheKey, not_null cache) const; - void validateVideo(); - void setStreamed(std::unique_ptr<::Media::Streaming::Instance> value); + bool videoAutoplayEnabled() const; + void setStreamed(std::unique_ptr value); void repaintStreamedContent(); - void checkStreamedIsStarted(); + void checkStreamedIsStarted() const; bool createStreamingObjects(); void handleStreamingUpdate(::Media::Streaming::Update &&update); void handleStreamingError(::Media::Streaming::Error &&error); void streamingReady(::Media::Streaming::Information &&info); - void paintUserpicFrame(Painter &p, QPoint photoPosition) const; + void paintUserpicFrame( + Painter &p, + QPoint photoPosition, + bool selected) const; not_null _data; int _serviceWidth = 0; @@ -129,7 +138,7 @@ private: int _pixh = 1; Ui::Text::String _caption; mutable std::shared_ptr _dataMedia; - mutable std::unique_ptr<::Media::Streaming::Instance> _streamed; + mutable std::unique_ptr _streamed; }; diff --git a/Telegram/SourceFiles/ui/special_buttons.cpp b/Telegram/SourceFiles/ui/special_buttons.cpp index 3ed369776c..b9345bb8f1 100644 --- a/Telegram/SourceFiles/ui/special_buttons.cpp +++ b/Telegram/SourceFiles/ui/special_buttons.cpp @@ -840,6 +840,7 @@ bool UserpicButton::createStreamingObjects(not_null photo) { _streamed = std::make_unique( photo->owner().streaming().sharedDocument(photo, origin), nullptr); + _streamed->lockPlayer(); _streamed->player().updates( ) | rpl::start_with_next_error([=](Update &&update) { handleStreamingUpdate(std::move(update));