Store cached frames in media local cache.

This commit is contained in:
John Preston 2019-06-27 16:36:01 +02:00
parent 5628c1eee6
commit 808583c5ae
8 changed files with 95 additions and 104 deletions

View File

@ -1093,10 +1093,25 @@ std::unique_ptr<Lottie::Animation> 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<void(QByteArray &&cached)> 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 });

View File

@ -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<Storage::Cache::Database*> cache,
Storage::Cache::Key key,
FnMut<void(QByteArray &&cached)> 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<Cache>(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<SharedState>(
content,
std::move(animation),
std::move(state),
cache,
key,
std::move(cache),
request))
: Error::ParseFailed;
}
@ -148,14 +144,14 @@ std::unique_ptr<Animation> FromContent(
}
std::unique_ptr<Animation> FromCached(
not_null<Storage::Cache::Database*> cache,
Storage::Cache::Key key,
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &data,
const QString &filepath,
const FrameRequest &request) {
const FrameRequest &request) {
return std::make_unique<Animation>(
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<Storage::Cache::Database*> cache,
Storage::Cache::Key key,
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> 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));
});

View File

@ -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<Animation> FromContent(
const QByteArray &data,
const QString &filepath);
std::unique_ptr<Animation> FromCached(
not_null<Storage::Cache::Database*> cache,
Storage::Cache::Key key,
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> 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<Storage::Cache::Database*> cache,
Storage::Cache::Key key,
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &content,
const FrameRequest &request);
~Animation();

View File

@ -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<void(QByteArray &&cached)> 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) != 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

View File

@ -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<void(QByteArray &&cached)> 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<void(QByteArray &&cached)> _put;
bool _changed = false;
};

View File

@ -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::Cache::Database*> storage,
Storage::Cache::Key key);
CacheState state;
not_null<Storage::Cache::Database*> storage;
Storage::Cache::Key key;
};
SharedState::Cache::Cache(
CacheState &&state,
not_null<Storage::Cache::Database*> 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<rlottie::Animation> animation)
SharedState::SharedState(
const QByteArray &content,
std::unique_ptr<rlottie::Animation> animation,
CacheState &&state,
not_null<Storage::Cache::Database*> cache,
Storage::Cache::Key key,
std::unique_ptr<Cache> cache,
const FrameRequest &request)
: _content(content)
, _animation(std::move(animation))
, _cache(std::make_unique<Cache>(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;
}
}

View File

@ -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<rlottie::Animation> animation,
CacheState &&state,
not_null<Storage::Cache::Database*> cache,
Storage::Cache::Key key,
std::unique_ptr<Cache> cache,
const FrameRequest &request);
void start(not_null<Animation*> owner, crl::time now);
@ -78,8 +69,6 @@ public:
~SharedState();
private:
struct Cache;
void construct(const FrameRequest &request);
void calculateProperties();
bool isValid() const;

View File

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