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<Storage::StreamedFileDownloader>(
 				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<Loader>
-#include "media/streaming/media_streaming_reader.h" // make_shared<Reader>
-#include "media/streaming/media_streaming_document.h" // make_shared<Document
 #include "boxes/abstract_box.h"
 #include "passport/passport_form_controller.h"
 #include "window/themes/window_theme.h"
@@ -48,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_poll.h"
 #include "data/data_scheduled_messages.h"
 #include "data/data_cloud_themes.h"
+#include "data/data_streaming.h"
 #include "base/platform/base_platform_info.h"
 #include "base/unixtime.h"
 #include "base/call_delayed.h"
@@ -172,27 +170,6 @@ rpl::producer<int> PinnedDialogsCountMaxValue(
 	});
 }
 
-template <typename Object>
-bool PruneDestroyedAndSet(
-		base::flat_map<
-			not_null<DocumentData*>,
-			std::weak_ptr<Object>> &objects,
-		not_null<DocumentData*> document,
-		const std::shared_ptr<Object> &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<Main::Session*> session)
@@ -213,7 +190,8 @@ Session::Session(not_null<Main::Session*> session)
 , _unmuteByFinishedTimer([=] { unmuteByFinished(); })
 , _groups(this)
 , _scheduledMessages(std::make_unique<ScheduledMessages>(this))
-, _cloudThemes(std::make_unique<CloudThemes>(session)) {
+, _cloudThemes(std::make_unique<CloudThemes>(session))
+, _streaming(std::make_unique<Streaming>(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<DocumentData*> 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<DocumentData*> 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<const PollData*> 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<const DocumentData*> document);
 	void requestPollViewRepaint(not_null<const PollData*> poll);
 
-	std::shared_ptr<::Media::Streaming::Reader> documentStreamedReader(
-		not_null<DocumentData*> document,
-		FileOrigin origin,
-		bool forceRemoteLoader = false);
-	std::shared_ptr<::Media::Streaming::Document> documentStreamer(
-		not_null<DocumentData*> document,
-		FileOrigin origin);
-
 	HistoryItem *addNewMessage(
 		const MTPMessage &data,
 		MTPDmessage_ClientFlags flags,
@@ -957,13 +943,6 @@ private:
 	base::flat_set<not_null<GameData*>> _gamesUpdated;
 	base::flat_set<not_null<PollData*>> _pollsUpdated;
 
-	base::flat_map<
-		not_null<DocumentData*>,
-		std::weak_ptr<::Media::Streaming::Reader>> _streamedReaders;
-	base::flat_map<
-		not_null<DocumentData*>,
-		std::weak_ptr<::Media::Streaming::Document>> _streamedDocuments;
-
 	base::flat_map<FolderId, std::unique_ptr<Folder>> _folders;
 	//rpl::variable<FeedId> _defaultFeedId = FeedId(); // #feed
 
@@ -1004,6 +983,7 @@ private:
 	Groups _groups;
 	std::unique_ptr<ScheduledMessages> _scheduledMessages;
 	std::unique_ptr<CloudThemes> _cloudThemes;
+	std::unique_ptr<Streaming> _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 <typename Object>
+bool PruneDestroyedAndSet(
+		base::flat_map<
+			not_null<DocumentData*>,
+			std::weak_ptr<Object>> &objects,
+		not_null<DocumentData*> document,
+		const std::shared_ptr<Object> &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<Session*> owner)
+: _owner(owner)
+, _keptAliveTimer([=] { clearKeptAlive(); }) {
+}
+
+Streaming::~Streaming() = default;
+
+std::shared_ptr<Streaming::Reader> Streaming::sharedReader(
+		not_null<DocumentData*> 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<Reader>(
+		&_owner->cacheBigFile(),
+		std::move(loader));
+	if (!PruneDestroyedAndSet(_readers, document, result)) {
+		_readers.emplace_or_assign(document, result);
+	}
+	return result;
+}
+
+std::shared_ptr<Streaming::Document> Streaming::sharedDocument(
+		not_null<DocumentData*> 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>(document, std::move(reader));
+	if (!PruneDestroyedAndSet(_documents, document, result)) {
+		_documents.emplace_or_assign(document, result);
+	}
+	return result;
+}
+
+void Streaming::keepAlive(not_null<DocumentData*> 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<crl::time>::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<Session*> 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<Reader> sharedReader(
+		not_null<DocumentData*> document,
+		FileOrigin origin,
+		bool forceRemoteLoader = false);
+	[[nodiscard]] std::shared_ptr<Document> sharedDocument(
+		not_null<DocumentData*> document,
+		FileOrigin origin);
+
+	void keepAlive(not_null<DocumentData*> document);
+
+private:
+	void clearKeptAlive();
+
+	const not_null<Session*> _owner;
+
+	base::flat_map<not_null<DocumentData*>, std::weak_ptr<Reader>> _readers;
+	base::flat_map<
+		not_null<DocumentData*>,
+		std::weak_ptr<Document>> _documents;
+
+	base::flat_map<std::shared_ptr<Document>, 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<Gif*>(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<Gif*>(this)->playAnimation(true);
-		}
+	const auto startPlay = autoplay && !_streamed;
+	if (startPlay) {
+		const_cast<Gif*>(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<void()> waitingCallback)
 : Instance(
-	document->owner().documentStreamer(document, origin),
+	document->owner().streaming().sharedDocument(document, origin),
 	std::move(waitingCallback)) {
 }