diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index e49b62047..1613253ad 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1071,6 +1071,8 @@ PRIVATE support/support_templates.h ui/chat/attach/attach_item_single_file_preview.cpp ui/chat/attach/attach_item_single_file_preview.h + ui/chat/attach/attach_item_single_media_preview.cpp + ui/chat/attach/attach_item_single_media_preview.h ui/effects/fireworks_animation.cpp ui/effects/fireworks_animation.h ui/effects/round_checkbox.cpp diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp new file mode 100644 index 000000000..0961d332a --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.cpp @@ -0,0 +1,216 @@ +/* +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/chat/attach/attach_item_single_media_preview.h" + +#include "data/data_document.h" +#include "data/data_document_media.h" +#include "data/data_file_origin.h" +#include "data/data_photo.h" +#include "data/data_photo_media.h" +#include "data/data_session.h" +#include "data/data_streaming.h" +#include "history/history_item.h" +#include "history/view/media/history_view_document.h" +#include "main/main_session.h" +#include "media/streaming/media_streaming_document.h" +#include "media/streaming/media_streaming_instance.h" +#include "media/streaming/media_streaming_loader_local.h" +#include "media/streaming/media_streaming_player.h" + +namespace Ui { +namespace { + +using namespace ::Media::Streaming; + +} // namespace + +ItemSingleMediaPreview::ItemSingleMediaPreview( + QWidget *parent, + Fn gifPaused, + not_null item) +: AbstractSingleMediaPreview(parent) +, _gifPaused(std::move(gifPaused)) +, _fullId(item->fullId()) { + const auto media = item->media(); + Assert(media != nullptr); + + Main::Session *session = nullptr; + + if (const auto photo = media->photo()) { + _photoMedia = photo->createMediaView(); + _photoMedia->wanted(Data::PhotoSize::Large, item->fullId()); + + session = &photo->session(); + } else if (const auto document = media->document()) { + _documentMedia = document->createMediaView(); + _documentMedia->thumbnailWanted(item->fullId()); + + session = &document->session(); + if (document->isAnimation() || document->isVideoFile()) { + setAnimated(true); + prepareStreamedPreview(); + } + } else { + Unexpected("Photo or document should be set."); + } + + struct ThumbInfo { + bool loaded = false; + Image *image = nullptr; + }; + + const auto computeThumbInfo = [=]() -> ThumbInfo { + using Size = Data::PhotoSize; + if (_documentMedia) { + return { true, _documentMedia->thumbnail() }; + } else if (const auto large = _photoMedia->image(Size::Large)) { + return { true, large }; + } else if (const auto thumbnail = _photoMedia->image( + Size::Thumbnail)) { + return { false, thumbnail }; + } else if (const auto small = _photoMedia->image(Size::Small)) { + return { false, small }; + } else { + return { false, _photoMedia->thumbnailInline() }; + } + }; + + rpl::single( + rpl::empty_value() + ) | rpl::then( + session->downloaderTaskFinished() + ) | rpl::start_with_next([=] { + const auto computed = computeThumbInfo(); + if (computed.loaded) { + _lifetimeDownload.destroy(); + } + preparePreview(computed.image->original()); + }, _lifetimeDownload); +} + +void ItemSingleMediaPreview::prepareStreamedPreview() { + if (_streamed || !_documentMedia) { + return; + } + const auto document = _documentMedia + ? _documentMedia->owner().get() + : nullptr; + if (document && document->isAnimation()) { + setupStreamedPreview( + document->owner().streaming().sharedDocument( + document, + _fullId)); + } +} + +void ItemSingleMediaPreview::setupStreamedPreview( + std::shared_ptr shared) { + if (!shared) { + return; + } + _streamed = std::make_unique( + std::move(shared), + [=] { update(); }); + _streamed->lockPlayer(); + _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())); + } + checkStreamedIsStarted(); +} + +void ItemSingleMediaPreview::handleStreamingUpdate(Update &&update) { + v::match(update.data, [&](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 ItemSingleMediaPreview::handleStreamingError(Error &&error) { +} + +void ItemSingleMediaPreview::streamingReady(Information &&info) { +} + +void ItemSingleMediaPreview::checkStreamedIsStarted() { + if (!_streamed) { + return; + } else if (_streamed->paused()) { + _streamed->resume(); + } + if (!_streamed->active() && !_streamed->failed()) { + startStreamedPlayer(); + } +} + +void ItemSingleMediaPreview::startStreamedPlayer() { + auto options = ::Media::Streaming::PlaybackOptions(); + options.audioId = _documentMedia + ? AudioMsgId(_documentMedia->owner(), _fullId) + : AudioMsgId(); + options.waitForMarkAsShown = true; + //if (!_streamed->withSound) { + options.mode = ::Media::Streaming::Mode::Video; + options.loop = true; + //} + _streamed->play(options); +} + +bool ItemSingleMediaPreview::drawBackground() const { + return true; // A sticker can't be here. +} + +bool ItemSingleMediaPreview::tryPaintAnimation(Painter &p) { + checkStreamedIsStarted(); + if (_streamed + && _streamed->player().ready() + && !_streamed->player().videoSize().isEmpty()) { + const auto s = QSize(previewWidth(), previewHeight()); + const auto paused = _gifPaused(); + + auto request = ::Media::Streaming::FrameRequest(); + request.outer = s * cIntRetinaFactor(); + request.resize = s * cIntRetinaFactor(); + p.drawImage( + QRect( + previewLeft(), + previewTop(), + previewWidth(), + previewHeight()), + _streamed->frame(request)); + if (!paused) { + _streamed->markFrameShown(); + } + return true; + } + return false; +} + +bool ItemSingleMediaPreview::isAnimatedPreviewReady() const { + return _streamed != nullptr; +} + +auto ItemSingleMediaPreview::sharedPhotoMedia() const +-> std::shared_ptr<::Data::PhotoMedia> { + return _photoMedia; +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.h b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.h new file mode 100644 index 000000000..170a10452 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/attach/attach_item_single_media_preview.h @@ -0,0 +1,68 @@ +/* +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 + +#include "ui/chat/attach/attach_abstract_single_media_preview.h" + +namespace Data { +class DocumentMedia; +class PhotoMedia; +} // namespace Data + +namespace Media { +namespace Streaming { +class Instance; +class Document; +struct Update; +enum class Error; +struct Information; +} // namespace Streaming +} // namespace Media + +class HistoryItem; + +namespace Ui { + +class ItemSingleMediaPreview final : public AbstractSingleMediaPreview { +public: + ItemSingleMediaPreview( + QWidget *parent, + Fn gifPaused, + not_null item); + + std::shared_ptr<::Data::PhotoMedia> sharedPhotoMedia() const; + +protected: + bool drawBackground() const override; + bool tryPaintAnimation(Painter &p) override; + bool isAnimatedPreviewReady() const override; + +private: + void prepareStreamedPreview(); + void checkStreamedIsStarted(); + void setupStreamedPreview( + std::shared_ptr<::Media::Streaming::Document> shared); + void handleStreamingUpdate(::Media::Streaming::Update &&update); + void handleStreamingError(::Media::Streaming::Error &&error); + void streamingReady(::Media::Streaming::Information &&info); + void startStreamedPlayer(); + + const Fn _gifPaused; + const FullMsgId _fullId; + + std::shared_ptr<::Data::PhotoMedia> _photoMedia; + std::shared_ptr<::Data::DocumentMedia> _documentMedia; + + std::unique_ptr<::Media::Streaming::Instance> _streamed; + + + rpl::lifetime _lifetimeDownload; + +}; + +} // namespace Ui