From 808583c5aefc8fc543fdcf52de9855b84b3efc09 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 27 Jun 2019 16:36:01 +0200 Subject: [PATCH] Store cached frames in media local cache. --- .../SourceFiles/chat_helpers/stickers.cpp | 21 ++++++- .../SourceFiles/lottie/lottie_animation.cpp | 34 +++++------ .../SourceFiles/lottie/lottie_animation.h | 15 ++--- Telegram/SourceFiles/lottie/lottie_cache.cpp | 56 ++++++++++++------- Telegram/SourceFiles/lottie/lottie_cache.h | 11 +++- .../lottie/lottie_frame_renderer.cpp | 45 ++++----------- .../lottie/lottie_frame_renderer.h | 15 +---- Telegram/gyp/lib_lottie.gyp | 2 - 8 files changed, 95 insertions(+), 104 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index e2f6b884f0..c7af4a5903 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -1093,10 +1093,25 @@ std::unique_ptr LottieFromDocument( QSize box) { const auto data = document->data(); const auto filepath = document->filepath(); - if (const auto key = document->bigFileBaseCacheKey()) { + if (const auto baseKey = document->bigFileBaseCacheKey()) { + const auto key = Storage::Cache::Key{ + baseKey->high, + baseKey->low + int(sizeTag) + }; + const auto get = [=](FnMut handler) { + document->session().data().cacheBigFile().get( + key, + std::move(handler)); + }; + const auto weak = base::make_weak(&document->session()); + const auto put = [=](QByteArray &&cached) { + crl::on_main(weak, [=, data = std::move(cached)]() mutable { + weak->data().cacheBigFile().put(key, std::move(data)); + }); + }; return Lottie::FromCached( - &document->session().data().cacheBigFile(), - Storage::Cache::Key{ key->high, key->low + int(sizeTag) }, + get, + put, data, filepath, Lottie::FrameRequest{ box }); diff --git a/Telegram/SourceFiles/lottie/lottie_animation.cpp b/Telegram/SourceFiles/lottie/lottie_animation.cpp index 3cde20be62..fb06bc6cfc 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.cpp +++ b/Telegram/SourceFiles/lottie/lottie_animation.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_frame_renderer.h" #include "lottie/lottie_cache.h" -#include "storage/cache/storage_cache_database.h" #include "base/algorithm.h" #include "zlib.h" #include "logs.h" @@ -101,24 +100,21 @@ details::InitData Init(const QByteArray &content) { details::InitData Init( const QByteArray &content, - not_null cache, - Storage::Cache::Key key, + FnMut put, const QByteArray &cached, const FrameRequest &request) { if (const auto error = ContentError(content)) { return *error; } - auto state = CacheState(cached, request); - const auto prepare = !state.framesCount() - || (state.framesReady() < state.framesCount()); + auto cache = std::make_unique(cached, request, std::move(put)); + const auto prepare = !cache->framesCount() + || (cache->framesReady() < cache->framesCount()); auto animation = prepare ? details::CreateFromContent(content) : nullptr; return (!prepare || animation) ? CheckSharedState(std::make_unique( content, std::move(animation), - std::move(state), - cache, - key, + std::move(cache), request)) : Error::ParseFailed; } @@ -148,14 +144,14 @@ std::unique_ptr FromContent( } std::unique_ptr FromCached( - not_null cache, - Storage::Cache::Key key, + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. const QByteArray &data, const QString &filepath, - const FrameRequest &request) { + const FrameRequest &request) { return std::make_unique( - cache, - key, + std::move(get), + std::move(put), ReadContent(data, filepath), request); } @@ -180,15 +176,15 @@ Animation::Animation(const QByteArray &content) } Animation::Animation( - not_null cache, - Storage::Cache::Key key, + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. const QByteArray &content, const FrameRequest &request) : _timer([=] { checkNextFrameRender(); }) { const auto weak = base::make_weak(this); - cache->get(key, [=](QByteArray &&cached) mutable { - crl::async([=] { - auto result = Init(content, cache, key, cached, request); + get([=, put = std::move(put)](QByteArray &&cached) mutable { + crl::async([=, put = std::move(put)]() mutable { + auto result = Init(content, std::move(put), cached, request); crl::on_main(weak, [=, data = std::move(result)]() mutable { initDone(std::move(data)); }); diff --git a/Telegram/SourceFiles/lottie/lottie_animation.h b/Telegram/SourceFiles/lottie/lottie_animation.h index fcd1c0b057..294dc46f5c 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.h +++ b/Telegram/SourceFiles/lottie/lottie_animation.h @@ -23,13 +23,6 @@ class QImage; class QString; class QByteArray; -namespace Storage { -namespace Cache { -class Database; -struct Key; -} // namespace Cache -} // namespace Storage - namespace rlottie { class Animation; } // namespace rlottie @@ -46,8 +39,8 @@ std::unique_ptr FromContent( const QByteArray &data, const QString &filepath); std::unique_ptr FromCached( - not_null cache, - Storage::Cache::Key key, + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. const QByteArray &data, const QString &filepath, const FrameRequest &request); @@ -67,8 +60,8 @@ class Animation final : public base::has_weak_ptr { public: explicit Animation(const QByteArray &content); Animation( - not_null cache, - Storage::Cache::Key key, + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. const QByteArray &content, const FrameRequest &request); ~Animation(); diff --git a/Telegram/SourceFiles/lottie/lottie_cache.cpp b/Telegram/SourceFiles/lottie/lottie_cache.cpp index bb4765522b..765ee11bfc 100644 --- a/Telegram/SourceFiles/lottie/lottie_cache.cpp +++ b/Telegram/SourceFiles/lottie/lottie_cache.cpp @@ -222,9 +222,13 @@ void DecodeAlpha(QImage &to, const EncodedStorage &from) { const auto till = ints + width; while (ints != till) { const auto value = uint32(*alpha++); - *ints = (*ints & 0x00FFFFFFU) | ((value & 0xF0U) << 24); + *ints = (*ints & 0x00FFFFFFU) + | ((value & 0xF0U) << 24) + | ((value & 0xF0U) << 20); ++ints; - *ints = (*ints & 0x00FFFFFFU) | (value << 28); + *ints = (*ints & 0x00FFFFFFU) + | (value << 28) + | ((value & 0x0FU) << 24); ++ints; } bytes += perLine; @@ -399,15 +403,19 @@ int EncodedStorage::aBytesPerLine() const { return _width / 2; } -CacheState::CacheState(const QByteArray &data, const FrameRequest &request) -: _data(data) { +Cache::Cache( + const QByteArray &data, + const FrameRequest &request, + FnMut put) +: _data(data) +, _put(std::move(put)) { if (!readHeader(request)) { _framesReady = 0; _data = QByteArray(); } } -void CacheState::init( +void Cache::init( QSize original, int frameRate, int framesCount, @@ -420,30 +428,30 @@ void CacheState::init( prepareBuffers(); } -int CacheState::frameRate() const { +int Cache::frameRate() const { return _frameRate; } -int CacheState::framesReady() const { +int Cache::framesReady() const { return _framesReady; } -int CacheState::framesCount() const { +int Cache::framesCount() const { return _framesCount; } -QSize CacheState::originalSize() const { +QSize Cache::originalSize() const { return _original; } -bool CacheState::readHeader(const FrameRequest &request) { +bool Cache::readHeader(const FrameRequest &request) { if (_data.isEmpty()) { return false; } QDataStream stream(&_data, QIODevice::ReadOnly); - auto encoder = quint8(0); + auto encoder = quint32(0); stream >> encoder; if (static_cast(encoder) != Encoder::YUV420A4_LZ4) { return false; @@ -481,11 +489,11 @@ bool CacheState::readHeader(const FrameRequest &request) { return renderFrame(_firstFrame, request, 0); } -QImage CacheState::takeFirstFrame() { +QImage Cache::takeFirstFrame() { return std::move(_firstFrame); } -bool CacheState::renderFrame( +bool Cache::renderFrame( QImage &to, const FrameRequest &request, int index) { @@ -516,7 +524,7 @@ bool CacheState::renderFrame( return true; } -void CacheState::appendFrame( +void Cache::appendFrame( const QImage &frame, const FrameRequest &request, int index) { @@ -547,7 +555,10 @@ void CacheState::appendFrame( } } -void CacheState::finalizeEncoding() { +void Cache::finalizeEncoding() { + if (_encode.compressedFrames.empty()) { + return; + } const auto size = (_data.isEmpty() ? headerSize() : _data.size()) + ranges::accumulate( _encode.compressedFrames, @@ -571,15 +582,16 @@ void CacheState::finalizeEncoding() { to += amount; } _encode = EncodeFields(); - constexpr auto test = sizeof(_encode); LOG(("SIZE: %1 (%2x%3, %4 frames, %5 xored)").arg(_data.size()).arg(_size.width()).arg(_size.height()).arg(_framesCount).arg(xored)); + + _put(QByteArray(_data)); } -int CacheState::headerSize() const { +int Cache::headerSize() const { return 8 * sizeof(qint32); } -void CacheState::writeHeader() { +void Cache::writeHeader() { Expects(_data.isEmpty()); QDataStream stream(&_data, QIODevice::WriteOnly); @@ -593,7 +605,7 @@ void CacheState::writeHeader() { << qint32(_framesReady); } -void CacheState::prepareBuffers() { +void Cache::prepareBuffers() { // 12 bit per pixel in YUV420P. const auto bytesPerLine = _size.width(); @@ -601,7 +613,7 @@ void CacheState::prepareBuffers() { _previous.allocate(bytesPerLine, _size.height()); } -CacheState::ReadResult CacheState::readCompressedFrame() { +Cache::ReadResult Cache::readCompressedFrame() { auto length = qint32(0); const auto part = bytes::make_span(_data).subspan(_offset); if (part.size() < sizeof(length)) { @@ -624,4 +636,8 @@ CacheState::ReadResult CacheState::readCompressedFrame() { return { ok, xored }; } +Cache::~Cache() { + finalizeEncoding(); +} + } // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_cache.h b/Telegram/SourceFiles/lottie/lottie_cache.h index 04863a013e..c352d31845 100644 --- a/Telegram/SourceFiles/lottie/lottie_cache.h +++ b/Telegram/SourceFiles/lottie/lottie_cache.h @@ -50,13 +50,16 @@ private: }; -class CacheState { +class Cache { public: enum class Encoder : qint8 { YUV420A4_LZ4, }; - CacheState(const QByteArray &data, const FrameRequest &request); + Cache( + const QByteArray &data, + const FrameRequest &request, + FnMut put); void init( QSize original, @@ -78,6 +81,8 @@ public: const FrameRequest &request, int index); + ~Cache(); + private: struct ReadResult { bool ok = false; @@ -112,6 +117,8 @@ private: int _offset = 0; int _offsetFrameIndex = 0; Encoder _encoder = Encoder::YUV420A4_LZ4; + FnMut _put; + bool _changed = false; }; diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp index da2c1a3dca..e960980853 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_animation.h" #include "lottie/lottie_cache.h" -#include "storage/cache/storage_cache_database.h" #include "logs.h" #include "rlottie.h" @@ -74,26 +73,6 @@ private: }; -struct SharedState::Cache { - Cache( - CacheState &&state, - not_null storage, - Storage::Cache::Key key); - - CacheState state; - not_null storage; - Storage::Cache::Key key; -}; - -SharedState::Cache::Cache( - CacheState &&state, - not_null storage, - Storage::Cache::Key key) -: state(std::move(state)) -, storage(storage) -, key(key) { -} - [[nodiscard]] bool GoodForRequest( const QImage &image, const FrameRequest &request) { @@ -206,13 +185,11 @@ SharedState::SharedState(std::unique_ptr animation) SharedState::SharedState( const QByteArray &content, std::unique_ptr animation, - CacheState &&state, - not_null cache, - Storage::Cache::Key key, + std::unique_ptr cache, const FrameRequest &request) : _content(content) , _animation(std::move(animation)) -, _cache(std::make_unique(std::move(state), cache, key)) { +, _cache(std::move(cache)) { construct(request); } @@ -221,13 +198,13 @@ void SharedState::construct(const FrameRequest &request) { if (!isValid()) { return; } - auto cover = _cache ? _cache->state.takeFirstFrame() : QImage(); + auto cover = _cache ? _cache->takeFirstFrame() : QImage(); if (!cover.isNull()) { init(std::move(cover), request); return; } if (_cache) { - _cache->state.init(_size, _frameRate, _framesCount, request); + _cache->init(_size, _frameRate, _framesCount, request); } renderFrame(cover, request, 0); init(std::move(cover), request); @@ -241,15 +218,15 @@ void SharedState::calculateProperties() { if (_animation) { _animation->size(width, height); } else { - width = _cache->state.originalSize().width(); - height = _cache->state.originalSize().height(); + width = _cache->originalSize().width(); + height = _cache->originalSize().height(); } const auto rate = _animation ? _animation->frameRate() - : _cache->state.frameRate(); + : _cache->frameRate(); const auto count = _animation ? _animation->totalFrame() - : _cache->state.framesCount(); + : _cache->framesCount(); _size = QSize( (width > 0 && width < kMaxSize) ? int(width) : 0, @@ -274,7 +251,7 @@ void SharedState::renderFrame( if (!GoodStorageForFrame(image, size)) { image = CreateFrameStorage(size); } - if (_cache && _cache->state.renderFrame(image, request, index)) { + if (_cache && _cache->renderFrame(image, request, index)) { return; } else if (!_animation) { _animation = details::CreateFromContent(_content); @@ -288,8 +265,8 @@ void SharedState::renderFrame( image.bytesPerLine()); _animation->renderSync(index, surface); if (_cache) { - _cache->state.appendFrame(image, request, index); - if (_cache->state.framesReady() == _cache->state.framesCount()) { + _cache->appendFrame(image, request, index); + if (_cache->framesReady() == _cache->framesCount()) { _animation = nullptr; } } diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h index 0e880756d5..c05cacd93b 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h @@ -21,13 +21,6 @@ namespace rlottie { class Animation; } // namespace rlottie -namespace Storage { -namespace Cache { -class Database; -struct Key; -} // namespace Cache -} // namespace Storage - namespace Lottie { inline constexpr auto kMaxFrameRate = 120; @@ -35,7 +28,7 @@ inline constexpr auto kMaxSize = 3096; inline constexpr auto kMaxFramesCount = 600; class Animation; -class CacheState; +class Cache; struct Frame { QImage original; @@ -57,9 +50,7 @@ public: SharedState( const QByteArray &content, std::unique_ptr animation, - CacheState &&state, - not_null cache, - Storage::Cache::Key key, + std::unique_ptr cache, const FrameRequest &request); void start(not_null owner, crl::time now); @@ -78,8 +69,6 @@ public: ~SharedState(); private: - struct Cache; - void construct(const FrameRequest &request); void calculateProperties(); bool isValid() const; diff --git a/Telegram/gyp/lib_lottie.gyp b/Telegram/gyp/lib_lottie.gyp index f191ee92a0..a6bd6f7951 100644 --- a/Telegram/gyp/lib_lottie.gyp +++ b/Telegram/gyp/lib_lottie.gyp @@ -30,7 +30,6 @@ 'crl.gyp:crl', 'lib_base.gyp:lib_base', 'lib_rlottie.gyp:lib_rlottie', - 'lib_storage.gyp:lib_storage', 'lib_ffmpeg.gyp:lib_ffmpeg', 'lib_lz4.gyp:lib_lz4', ], @@ -38,7 +37,6 @@ 'crl.gyp:crl', 'lib_base.gyp:lib_base', 'lib_rlottie.gyp:lib_rlottie', - 'lib_storage.gyp:lib_storage', 'lib_ffmpeg.gyp:lib_ffmpeg', 'lib_lz4.gyp:lib_lz4', ],