Save streaming player between message edits.

This commit is contained in:
John Preston 2019-12-28 17:56:06 +03:00
parent a980fba3aa
commit d47c138f23
9 changed files with 226 additions and 112 deletions

View File

@ -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

View File

@ -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,

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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)) {
}