From d47c138f23b2531b6e74bf68d54bb3a28ef34d86 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 28 Dec 2019 17:56:06 +0300 Subject: [PATCH] Save streaming player between message edits. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/data/data_document.cpp | 3 +- Telegram/SourceFiles/data/data_session.cpp | 75 +--------- Telegram/SourceFiles/data/data_session.h | 30 +--- Telegram/SourceFiles/data/data_streaming.cpp | 137 ++++++++++++++++++ Telegram/SourceFiles/data/data_streaming.h | 61 ++++++++ .../history/view/media/history_view_gif.cpp | 24 +-- .../media/player/media_player_instance.cpp | 3 +- .../streaming/media_streaming_instance.cpp | 3 +- 9 files changed, 226 insertions(+), 112 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_streaming.cpp create mode 100644 Telegram/SourceFiles/data/data_streaming.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 5567ef226..48eacfe81 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -333,6 +333,8 @@ PRIVATE data/data_shared_media.h data/data_sparse_ids.cpp data/data_sparse_ids.h + data/data_streaming.cpp + data/data_streaming.h data/data_types.cpp data/data_types.h data/data_user.cpp diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 831f764e5..59bc935e0 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" +#include "data/data_streaming.h" #include "data/data_document_good_thumbnail.h" #include "lang/lang_keys.h" #include "inline_bots/inline_bot_layout_item.h" @@ -931,7 +932,7 @@ void DocumentData::save( } } else { status = FileReady; - auto reader = owner().documentStreamedReader(this, origin, true); + auto reader = owner().streaming().sharedReader(this, origin, true); if (reader) { _loader = std::make_unique( id, diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index dcef06431..5816833a9 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -28,9 +28,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_encrypted_file.h" #include "main/main_account.h" #include "media/player/media_player_instance.h" // instance()->play() -#include "media/streaming/media_streaming_loader.h" // unique_ptr -#include "media/streaming/media_streaming_reader.h" // make_shared -#include "media/streaming/media_streaming_document.h" // make_shared PinnedDialogsCountMaxValue( }); } -template -bool PruneDestroyedAndSet( - base::flat_map< - not_null, - std::weak_ptr> &objects, - not_null document, - const std::shared_ptr &object) { - auto result = false; - for (auto i = begin(objects); i != end(objects);) { - if (i->first == document) { - (i++)->second = object; - result = true; - } else if (i->second.lock() != nullptr) { - ++i; - } else { - i = objects.erase(i); - } - } - return result; -} - } // namespace Session::Session(not_null session) @@ -213,7 +190,8 @@ Session::Session(not_null session) , _unmuteByFinishedTimer([=] { unmuteByFinished(); }) , _groups(this) , _scheduledMessages(std::make_unique(this)) -, _cloudThemes(std::make_unique(session)) { +, _cloudThemes(std::make_unique(session)) +, _streaming(std::make_unique(this)) { _cache->open(Local::cacheKey()); _bigFileCache->open(Local::cacheBigFileKey()); @@ -1133,53 +1111,6 @@ void Session::requestDocumentViewRepaint( } } -std::shared_ptr<::Media::Streaming::Reader> Session::documentStreamedReader( - not_null document, - FileOrigin origin, - bool forceRemoteLoader) { - const auto i = _streamedReaders.find(document); - if (i != end(_streamedReaders)) { - if (auto result = i->second.lock()) { - if (!forceRemoteLoader || result->isRemoteLoader()) { - return result; - } - } - } - auto loader = document->createStreamingLoader(origin, forceRemoteLoader); - if (!loader) { - return nullptr; - } - auto result = std::make_shared<::Media::Streaming::Reader>( - &cacheBigFile(), - std::move(loader)); - if (!PruneDestroyedAndSet(_streamedReaders, document, result)) { - _streamedReaders.emplace_or_assign(document, result); - } - return result; -} - -std::shared_ptr<::Media::Streaming::Document> Session::documentStreamer( - not_null document, - FileOrigin origin) { - const auto i = _streamedDocuments.find(document); - if (i != end(_streamedDocuments)) { - if (auto result = i->second.lock()) { - return result; - } - } - auto reader = documentStreamedReader(document, origin); - if (!reader) { - return nullptr; - } - auto result = std::make_shared<::Media::Streaming::Document>( - document, - std::move(reader)); - if (!PruneDestroyedAndSet(_streamedDocuments, document, result)) { - _streamedDocuments.emplace_or_assign(document, result); - } - return result; -} - void Session::requestPollViewRepaint(not_null poll) { if (const auto i = _pollViews.find(poll); i != _pollViews.end()) { for (const auto view : i->second) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index d9a5d13a7..eb21a0b0e 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -37,16 +37,6 @@ namespace Main { class Session; } // namespace Main -namespace Media { -namespace Clip { -class Reader; -} // namespace Clip -namespace Streaming { -class Reader; -class Document; -} // namespace Streaming -} // namespace Media - namespace Export { class Controller; namespace View { @@ -69,6 +59,7 @@ class LocationPoint; class WallPaper; class ScheduledMessages; class CloudThemes; +class Streaming; class Session final { public: @@ -98,6 +89,9 @@ public: [[nodiscard]] CloudThemes &cloudThemes() const { return *_cloudThemes; } + [[nodiscard]] Streaming &streaming() const { + return *_streaming; + } [[nodiscard]] MsgId nextNonHistoryEntryId() { return ++_nonHistoryEntryId; } @@ -436,14 +430,6 @@ public: void markMediaRead(not_null document); void requestPollViewRepaint(not_null poll); - std::shared_ptr<::Media::Streaming::Reader> documentStreamedReader( - not_null document, - FileOrigin origin, - bool forceRemoteLoader = false); - std::shared_ptr<::Media::Streaming::Document> documentStreamer( - not_null document, - FileOrigin origin); - HistoryItem *addNewMessage( const MTPMessage &data, MTPDmessage_ClientFlags flags, @@ -957,13 +943,6 @@ private: base::flat_set> _gamesUpdated; base::flat_set> _pollsUpdated; - base::flat_map< - not_null, - std::weak_ptr<::Media::Streaming::Reader>> _streamedReaders; - base::flat_map< - not_null, - std::weak_ptr<::Media::Streaming::Document>> _streamedDocuments; - base::flat_map> _folders; //rpl::variable _defaultFeedId = FeedId(); // #feed @@ -1004,6 +983,7 @@ private: Groups _groups; std::unique_ptr _scheduledMessages; std::unique_ptr _cloudThemes; + std::unique_ptr _streaming; MsgId _nonHistoryEntryId = ServerMaxMsgId; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/data/data_streaming.cpp b/Telegram/SourceFiles/data/data_streaming.cpp new file mode 100644 index 000000000..205f908c5 --- /dev/null +++ b/Telegram/SourceFiles/data/data_streaming.cpp @@ -0,0 +1,137 @@ +/* +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 "data/data_streaming.h" + +#include "data/data_document.h" +#include "data/data_session.h" +#include "data/data_file_origin.h" +#include "media/streaming/media_streaming_loader.h" +#include "media/streaming/media_streaming_reader.h" +#include "media/streaming/media_streaming_document.h" + +namespace Data { +namespace { + +constexpr auto kKeepAliveTimeout = 5 * crl::time(1000); + +template +bool PruneDestroyedAndSet( + base::flat_map< + not_null, + std::weak_ptr> &objects, + not_null document, + const std::shared_ptr &object) { + auto result = false; + for (auto i = begin(objects); i != end(objects);) { + if (i->first == document) { + (i++)->second = object; + result = true; + } else if (i->second.lock() != nullptr) { + ++i; + } else { + i = objects.erase(i); + } + } + return result; +} + +} // namespace + +Streaming::Streaming(not_null owner) +: _owner(owner) +, _keptAliveTimer([=] { clearKeptAlive(); }) { +} + +Streaming::~Streaming() = default; + +std::shared_ptr Streaming::sharedReader( + not_null document, + FileOrigin origin, + bool forceRemoteLoader) { + const auto i = _readers.find(document); + if (i != end(_readers)) { + if (auto result = i->second.lock()) { + if (!forceRemoteLoader || result->isRemoteLoader()) { + return result; + } + } + } + auto loader = document->createStreamingLoader(origin, forceRemoteLoader); + if (!loader) { + return nullptr; + } + auto result = std::make_shared( + &_owner->cacheBigFile(), + std::move(loader)); + if (!PruneDestroyedAndSet(_readers, document, result)) { + _readers.emplace_or_assign(document, result); + } + return result; +} + +std::shared_ptr Streaming::sharedDocument( + not_null document, + FileOrigin origin) { + const auto i = _documents.find(document); + if (i != end(_documents)) { + if (auto result = i->second.lock()) { + return result; + } + } + auto reader = sharedReader(document, origin); + if (!reader) { + return nullptr; + } + auto result = std::make_shared(document, std::move(reader)); + if (!PruneDestroyedAndSet(_documents, document, result)) { + _documents.emplace_or_assign(document, result); + } + return result; +} + +void Streaming::keepAlive(not_null document) { + const auto i = _documents.find(document); + if (i == end(_documents)) { + return; + } + auto shared = i->second.lock(); + if (!shared) { + return; + } + const auto till = crl::now() + kKeepAliveTimeout; + const auto j = _keptAlive.find(shared); + if (j != end(_keptAlive)) { + j->second = till; + } else { + _keptAlive.emplace(std::move(shared), till); + } + if (!_keptAliveTimer.isActive()) { + _keptAliveTimer.callOnce(kKeepAliveTimeout); + } +} + +void Streaming::clearKeptAlive() { + const auto now = crl::now(); + auto min = std::numeric_limits::max(); + for (auto i = begin(_keptAlive); i != end(_keptAlive);) { + const auto wait = (i->second - now); + if (wait <= 0) { + i = _keptAlive.erase(i); + } else { + ++i; + if (min > wait) { + min = wait; + } + } + } + if (!_keptAlive.empty()) { + _keptAliveTimer.callOnce(min); + } +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_streaming.h b/Telegram/SourceFiles/data/data_streaming.h new file mode 100644 index 000000000..9f7112842 --- /dev/null +++ b/Telegram/SourceFiles/data/data_streaming.h @@ -0,0 +1,61 @@ +/* +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 "base/timer.h" + +class DocumentData; + +namespace Media { +namespace Streaming { +class Reader; +class Document; +} // namespace Streaming +} // namespace Media + +namespace Data { + +class Session; +struct FileOrigin; + +class Streaming final { +public: + explicit Streaming(not_null owner); + Streaming(const Streaming &other) = delete; + Streaming &operator=(const Streaming &other) = delete; + ~Streaming(); + + using Reader = ::Media::Streaming::Reader; + using Document = ::Media::Streaming::Document; + + [[nodiscard]] std::shared_ptr sharedReader( + not_null document, + FileOrigin origin, + bool forceRemoteLoader = false); + [[nodiscard]] std::shared_ptr sharedDocument( + not_null document, + FileOrigin origin); + + void keepAlive(not_null document); + +private: + void clearKeptAlive(); + + const not_null _owner; + + base::flat_map, std::weak_ptr> _readers; + base::flat_map< + not_null, + std::weak_ptr> _documents; + + base::flat_map, crl::time> _keptAlive; + base::Timer _keptAliveTimer; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index b40cc5183..f1616b6cd 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/grouped_layout.h" #include "data/data_session.h" +#include "data/data_streaming.h" #include "data/data_document.h" #include "data/data_file_origin.h" #include "app.h" @@ -81,7 +82,10 @@ Gif::Gif( } Gif::~Gif() { - setStreamed(nullptr); + if (_streamed) { + _data->owner().streaming().keepAlive(_data); + setStreamed(nullptr); + } } QSize Gif::sizeForAspectRatio() const { @@ -256,13 +260,11 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms const auto canBePlayed = _data->canBePlayed(); const auto autoplay = autoplayEnabled() && canBePlayed; const auto activeRoundPlaying = activeRoundStreamed(); - const auto startPlayAsync = autoplay + const auto startPlay = autoplay && !_streamed && !activeRoundPlaying; - if (startPlayAsync) { - if (!autoPaused) { - _parent->delegate()->elementAnimationAutoplayAsync(_parent); - } + if (startPlay) { + const_cast(this)->playAnimation(true); } else { checkStreamedIsStarted(); } @@ -857,11 +859,9 @@ void Gif::drawGrouped( const auto cornerDownload = fullFeatured && downloadInCorner(); const auto canBePlayed = _data->canBePlayed(); const auto autoplay = fullFeatured && autoplayEnabled() && canBePlayed; - const auto startPlayAsync = autoplay && !_streamed; - if (startPlayAsync) { - if (!autoPaused) { - const_cast(this)->playAnimation(true); - } + const auto startPlay = autoplay && !_streamed; + if (startPlay) { + const_cast(this)->playAnimation(true); } else { checkStreamedIsStarted(); } @@ -1301,7 +1301,7 @@ void Gif::playAnimation(bool autoplay) { } void Gif::createStreamedPlayer() { - auto shared = _data->owner().documentStreamer( + auto shared = _data->owner().streaming().sharedDocument( _data, _realParent->fullId()); if (!shared) { diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 8b69789ba..217d631b0 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" +#include "data/data_streaming.h" #include "media/audio/media_audio.h" #include "media/audio/media_audio_capture.h" #include "media/streaming/media_streaming_instance.h" @@ -359,7 +360,7 @@ void Instance::play(const AudioMsgId &audioId) { if (document->isAudioFile() || document->isVoiceMessage() || document->isVideoMessage()) { - auto shared = document->owner().documentStreamer( + auto shared = document->owner().streaming().sharedDocument( document, audioId.contextId()); if (!shared) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp index a6082e86e..78902dbc2 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_document.h" #include "data/data_session.h" +#include "data/data_streaming.h" namespace Media { namespace Streaming { @@ -30,7 +31,7 @@ Instance::Instance( Data::FileOrigin origin, Fn waitingCallback) : Instance( - document->owner().documentStreamer(document, origin), + document->owner().streaming().sharedDocument(document, origin), std::move(waitingCallback)) { }