From 9e4ac1c835cd91a2dc04db6286591d6878eccfa1 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 9 Oct 2025 10:15:15 +0300 Subject: [PATCH] Created simple class for video userpic processing. --- Telegram/CMakeLists.txt | 2 + .../ui/peer/video_userpic_player.cpp | 172 ++++++++++++++++++ .../ui/peer/video_userpic_player.h | 51 ++++++ 3 files changed, 225 insertions(+) create mode 100644 Telegram/SourceFiles/ui/peer/video_userpic_player.cpp create mode 100644 Telegram/SourceFiles/ui/peer/video_userpic_player.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index b143a03161..797296d1ac 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1675,6 +1675,8 @@ PRIVATE ui/resize_area.h ui/unread_badge.cpp ui/unread_badge.h + ui/peer/video_userpic_player.cpp + ui/peer/video_userpic_player.h window/main_window.cpp window/main_window.h window/notifications_manager.cpp diff --git a/Telegram/SourceFiles/ui/peer/video_userpic_player.cpp b/Telegram/SourceFiles/ui/peer/video_userpic_player.cpp new file mode 100644 index 0000000000..2ea145bf1c --- /dev/null +++ b/Telegram/SourceFiles/ui/peer/video_userpic_player.cpp @@ -0,0 +1,172 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/peer/video_userpic_player.h" + +#include "data/data_peer.h" +#include "data/data_photo.h" +#include "data/data_session.h" +#include "data/data_streaming.h" +#include "data/data_file_origin.h" +#include "media/streaming/media_streaming_instance.h" +#include "media/streaming/media_streaming_player.h" +#include "media/streaming/media_streaming_document.h" +#include "ui/image/image_prepare.h" +#include "ui/empty_userpic.h" +#include "ui/painter.h" +#include "styles/style_widgets.h" + +namespace Ui { + +VideoUserpicPlayer::VideoUserpicPlayer() = default; + +VideoUserpicPlayer::~VideoUserpicPlayer() = default; + +void VideoUserpicPlayer::setup( + not_null peer, + not_null photo) { + if (_streamedPhoto == photo && _streamed && _peer == peer) { + return; + } + if (!createStreaming(peer, photo)) { + photo->setVideoPlaybackFailed(); + return; + } + _streamedPhoto = photo; + _peer = peer; + checkStarted(); +} + +void VideoUserpicPlayer::clear() { + _streamed = nullptr; + _streamedPhoto = nullptr; + _peer = nullptr; +} + +QImage VideoUserpicPlayer::frame(QSize size, not_null peer) { + if (!_streamed + || !_streamed->player().ready() + || _streamed->player().videoSize().isEmpty() + || !_streamedPhoto) { + return {}; + } + auto request = ::Media::Streaming::FrameRequest(); + const auto ratio = style::DevicePixelRatio(); + request.outer = request.resize = size * ratio; + + const auto broadcast = peer->monoforumBroadcast(); + + if (broadcast) { + if (_monoforumMask.isNull()) { + _monoforumMask = Ui::MonoforumShapeMask(request.resize); + } + } else if (peer->isForum()) { + const auto radius = int( + size.width() * Ui::ForumUserpicRadiusMultiplier()); + if (_roundingCorners[0].width() != radius * ratio) { + _roundingCorners = Images::CornersMask(radius); + } + request.rounding = Images::CornersMaskRef(_roundingCorners); + } else { + if (_ellipseMask.size() != request.outer) { + _ellipseMask = Images::EllipseMask(size); + } + request.mask = _ellipseMask; + } + + auto result = _streamed->frame(request); + if (broadcast) { + constexpr auto kFormat = QImage::Format_ARGB32_Premultiplied; + if (result.format() != kFormat) { + result = std::move(result).convertToFormat(kFormat); + } + auto q = QPainter(&result); + q.setCompositionMode(QPainter::CompositionMode_DestinationIn); + q.drawImage( + QRect(QPoint(), result.size() / result.devicePixelRatio()), + _monoforumMask); + q.end(); + } + _streamed->markFrameShown(); + return result; +} + +bool VideoUserpicPlayer::ready() const { + return _streamed + && _streamed->player().ready() + && !_streamed->player().videoSize().isEmpty(); +} + +bool VideoUserpicPlayer::createStreaming( + not_null peer, + not_null photo) { + using namespace ::Media::Streaming; + const auto origin = peer->isUser() + ? Data::FileOriginUserPhoto(peerToUser(peer->id), photo->id) + : Data::FileOrigin(Data::FileOriginPeerPhoto(peer->id)); + _streamed = std::make_unique( + photo->owner().streaming().sharedDocument(photo, origin), + nullptr); + _streamed->lockPlayer(); + _streamed->player().updates( + ) | rpl::start_with_next_error([=](Update &&update) { + handleUpdate(std::move(update)); + }, [=](Error &&error) { + handleError(std::move(error)); + }, _streamed->lifetime()); + if (_streamed->ready()) { + streamingReady(base::duplicate(_streamed->info())); + } + if (!_streamed->valid()) { + clear(); + return false; + } + return true; +} + +void VideoUserpicPlayer::checkStarted() { + 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 VideoUserpicPlayer::handleUpdate( + ::Media::Streaming::Update &&update) { + using namespace ::Media::Streaming; + v::match(update.data, [&](Information &update) { + streamingReady(std::move(update)); + }, [](PreloadedVideo) { + }, [](UpdateVideo) { + }, [](PreloadedAudio) { + }, [](UpdateAudio) { + }, [](WaitingForData) { + }, [](SpeedEstimate) { + }, [](MutedByOther) { + }, [](Finished) { + }); +} + +void VideoUserpicPlayer::handleError(::Media::Streaming::Error &&error) { + _streamedPhoto->setVideoPlaybackFailed(); + clear(); +} + +void VideoUserpicPlayer::streamingReady( + ::Media::Streaming::Information &&info) { +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/peer/video_userpic_player.h b/Telegram/SourceFiles/ui/peer/video_userpic_player.h new file mode 100644 index 0000000000..4f7d1de4e6 --- /dev/null +++ b/Telegram/SourceFiles/ui/peer/video_userpic_player.h @@ -0,0 +1,51 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Media::Streaming { +class Instance; +struct Update; +struct Information; +enum class Error; +} // namespace Media::Streaming + +class PhotoData; +class PeerData; + +namespace Ui { + +class VideoUserpicPlayer final { +public: + VideoUserpicPlayer(); + ~VideoUserpicPlayer(); + + void setup(not_null peer, not_null photo); + void clear(); + + [[nodiscard]] QImage frame(QSize size, not_null peer); + [[nodiscard]] bool ready() const; + +private: + bool createStreaming( + not_null peer, + not_null photo); + void checkStarted(); + void handleUpdate(::Media::Streaming::Update &&update); + void handleError(::Media::Streaming::Error &&error); + void streamingReady(::Media::Streaming::Information &&info); + + std::unique_ptr<::Media::Streaming::Instance> _streamed; + PhotoData *_streamedPhoto = nullptr; + QImage _ellipseMask; + QImage _monoforumMask; + std::array _roundingCorners; + PeerData *_peer = nullptr; + +}; + +} // namespace Ui