diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 3a9c7c82f..3aec102e9 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -816,8 +816,7 @@ int EditCaptionBox::errorTopSkip() const { void EditCaptionBox::checkStreamedIsStarted() { if (!_streamed) { return; - } - if (_streamed->paused()) { + } else if (_streamed->paused()) { _streamed->resume(); } if (!_streamed->active() && !_streamed->failed()) { diff --git a/Telegram/SourceFiles/ui/special_buttons.cpp b/Telegram/SourceFiles/ui/special_buttons.cpp index a450730ea..3d92c159c 100644 --- a/Telegram/SourceFiles/ui/special_buttons.cpp +++ b/Telegram/SourceFiles/ui/special_buttons.cpp @@ -21,11 +21,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_cloud_file.h" #include "data/data_changes.h" +#include "data/data_user.h" +#include "data/data_streaming.h" +#include "data/data_file_origin.h" #include "history/history.h" #include "core/file_utilities.h" #include "core/application.h" #include "boxes/photo_crop_box.h" #include "boxes/confirm_box.h" +#include "media/streaming/media_streaming_instance.h" +#include "media/streaming/media_streaming_player.h" +#include "media/streaming/media_streaming_document.h" #include "window/window_session_controller.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -582,7 +588,7 @@ void UserpicButton::setClickHandlerByRole() { break; case Role::OpenPhoto: - addClickHandler([this] { + addClickHandler([=] { openPeerPhoto(); }); break; @@ -674,7 +680,7 @@ void UserpicButton::paintEvent(QPaintEvent *e) { p.drawPixmapLeft(photoPosition, width(), _oldUserpic); p.setOpacity(_a_appearance.value(1.)); } - p.drawPixmapLeft(photoPosition, width(), _userpic); + paintUserpicFrame(p, photoPosition); } if (_role == Role::ChangePhoto) { @@ -754,6 +760,27 @@ void UserpicButton::paintEvent(QPaintEvent *e) { } } +void UserpicButton::paintUserpicFrame(Painter &p, QPoint photoPosition) { + checkStreamedIsStarted(); + if (_streamed + && _streamed->player().ready() + && !_streamed->player().videoSize().isEmpty()) { + const auto paused = _controller->isGifPausedAtLeastFor( + Window::GifPauseReason::RoundPlaying); + auto request = Media::Streaming::FrameRequest(); + auto size = QSize{ _st.photoSize, _st.photoSize }; + request.outer = size * cIntRetinaFactor(); + request.resize = size * cIntRetinaFactor(); + request.radius = ImageRoundRadius::Ellipse; + p.drawImage(QRect(photoPosition, size), _streamed->frame(request)); + if (!paused) { + _streamed->markFrameShown(); + } + } else { + p.drawPixmapLeft(photoPosition, width(), _userpic); + } +} + QPoint UserpicButton::countPhotoPosition() const { auto photoLeft = (_st.photoPosition.x() < 0) ? (width() - _st.photoSize) / 2 @@ -790,6 +817,7 @@ void UserpicButton::processPeerPhoto() { } _canOpenPhoto = (_peer->userpicPhotoId() != 0); updateCursor(); + updateVideo(); } } @@ -801,6 +829,109 @@ void UserpicButton::updateCursor() { setPointerCursor(pointer); } +bool UserpicButton::createStreamingObjects(not_null photo) { + Expects(_peer != nullptr); + + using namespace Media::Streaming; + + const auto origin = _peer->isUser() + ? Data::FileOriginUserPhoto(_peer->asUser()->bareId(), photo->id) + : Data::FileOrigin(Data::FileOriginPeerPhoto(_peer->id)); + _streamed = std::make_unique( + photo->owner().streaming().sharedDocument(photo, origin), + [=] { update(); }); + _streamed->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())); + } + if (!_streamed->valid()) { + clearStreaming(); + return false; + } + return true; +} + +void UserpicButton::clearStreaming() { + _streamed = nullptr; + _streamedPhoto = nullptr; +} + +void UserpicButton::handleStreamingUpdate(Media::Streaming::Update &&update) { + using namespace Media::Streaming; + + update.data.match([&](Information &update) { + streamingReady(std::move(update)); + }, [&](const PreloadedVideo &update) { + }, [&](const UpdateVideo &update) { + this->update(); + }, [&](const PreloadedAudio &update) { + }, [&](const UpdateAudio &update) { + }, [&](const WaitingForData &update) { + }, [&](MutedByOther) { + }, [&](Finished) { + }); +} + +void UserpicButton::handleStreamingError(Media::Streaming::Error &&error) { + Expects(_peer != nullptr); + + _streamedPhoto->setVideoPlaybackFailed(); + _streamedPhoto = nullptr; + _streamed = nullptr; +} + +void UserpicButton::streamingReady(Media::Streaming::Information &&info) { + update(); +} + +void UserpicButton::updateVideo() { + Expects(_role == Role::OpenPhoto); + + using namespace Media::Streaming; + + const auto id = _peer->userpicPhotoId(); + if (!id) { + clearStreaming(); + return; + } + const auto photo = _peer->owner().photo(id); + if (!photo->date || !photo->videoCanBePlayed()) { + clearStreaming(); + return; + } else if (_streamed && _streamedPhoto == photo) { + return; + } + if (!createStreamingObjects(photo)) { + photo->setVideoPlaybackFailed(); + return; + } + _streamedPhoto = photo; + checkStreamedIsStarted(); +} + +void UserpicButton::checkStreamedIsStarted() { + Expects(!_streamed || _streamedPhoto); + + if (!_streamed) { + return; + } else if (_streamed->paused()) { + _streamed->resume(); + } + if (_streamed && !_streamed->active() && !_streamed->failed()) { + const auto position = _streamedPhoto->videoStartPosition(); + auto options = Media::Streaming::PlaybackOptions(); + options.position = position; + options.mode = Media::Streaming::Mode::Video; + options.loop = true; + _streamed->play(options); + } +} + void UserpicButton::mouseMoveEvent(QMouseEvent *e) { RippleButton::mouseMoveEvent(e); if (_role == Role::OpenPhoto) { diff --git a/Telegram/SourceFiles/ui/special_buttons.h b/Telegram/SourceFiles/ui/special_buttons.h index 6f2cca86c..19546c23a 100644 --- a/Telegram/SourceFiles/ui/special_buttons.h +++ b/Telegram/SourceFiles/ui/special_buttons.h @@ -23,6 +23,16 @@ namespace Window { class SessionController; } // namespace Window +namespace Media { +namespace Streaming { +class Instance; +class Document; +struct Update; +enum class Error; +struct Information; +} // namespace Streaming +} // namespace Media + namespace Ui { class InfiniteRadialAnimation; @@ -211,7 +221,15 @@ private: void updateCursorInChangeOverlay(QPoint localPos); void setCursorInChangeOverlay(bool inOverlay); void updateCursor(); + void updateVideo(); + void checkStreamedIsStarted(); + bool createStreamingObjects(not_null photo); + void clearStreaming(); + void handleStreamingUpdate(Media::Streaming::Update &&update); + void handleStreamingError(Media::Streaming::Error &&error); + void streamingReady(Media::Streaming::Information &&info); bool showSavedMessages() const; + void paintUserpicFrame(Painter &p, QPoint photoPosition); void grabOldUserpic(); void setClickHandlerByRole(); @@ -233,6 +251,8 @@ private: InMemoryKey _userpicUniqueKey; Ui::Animations::Simple _a_appearance; QImage _result; + std::unique_ptr _streamed; + PhotoData *_streamedPhoto = nullptr; bool _showSavedMessagesOnSelf = false; bool _canOpenPhoto = false;