diff --git a/Telegram/SourceFiles/base/algorithm.h b/Telegram/SourceFiles/base/algorithm.h index 62ca24e47..9038d24de 100644 --- a/Telegram/SourceFiles/base/algorithm.h +++ b/Telegram/SourceFiles/base/algorithm.h @@ -30,6 +30,44 @@ inline bool contains(const Container &container, const T &value) { return std::find(std::begin(container), end, value) != end; } +// We need a custom comparator for set>::find to work with pointers. +// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs +template +struct pointer_comparator { + using is_transparent = std::true_type; + + // helper does some magic in order to reduce the number of + // pairs of types we need to know how to compare: it turns + // everything into a pointer, and then uses `std::less` + // to do the comparison: + struct helper { + const T *ptr = nullptr; + helper() = default; + helper(const helper &other) = default; + helper(const T *p) : ptr(p) { + } + template + helper(const std::shared_ptr &other) : ptr(other.get()) { + } + template + helper(const std::unique_ptr &other) : ptr(other.get()) { + } + bool operator<(helper other) const { + return std::less()(ptr, other.ptr); + } + }; + + // without helper, we'd need 2^n different overloads, where + // n is the number of types we want to support (so, 8 with + // raw pointers, unique pointers, and shared pointers). That + // seems silly. + // && helps enforce rvalue use only + bool operator()(const helper &&lhs, const helper &&rhs) const { + return lhs < rhs; + } + +}; + } // namespace base template diff --git a/Telegram/SourceFiles/base/flat_map.h b/Telegram/SourceFiles/base/flat_map.h index 2f80a7219..18a9ede3d 100644 --- a/Telegram/SourceFiles/base/flat_map.h +++ b/Telegram/SourceFiles/base/flat_map.h @@ -53,12 +53,19 @@ struct flat_multi_map_pair_type { , second(std::forward(value)) { } - flat_multi_map_pair_type(const flat_multi_map_pair_type&) = default; - flat_multi_map_pair_type(flat_multi_map_pair_type&&) = default; + flat_multi_map_pair_type(const flat_multi_map_pair_type &pair) + : first(pair.first) + , second(pair.second) { + } + + flat_multi_map_pair_type(flat_multi_map_pair_type &&pair) + : first(std::move(const_cast(pair.first))) + , second(std::move(pair.second)) { + } flat_multi_map_pair_type &operator=(const flat_multi_map_pair_type&) = delete; flat_multi_map_pair_type &operator=(flat_multi_map_pair_type &&other) { - const_cast(first) = other.first; + const_cast(first) = std::move(const_cast(other.first)); second = std::move(other.second); return *this; } @@ -474,6 +481,28 @@ public: return compare()(key, where->first) ? impl().end() : where; } + template + iterator findFirst(const OtherKey &key) { + if (empty() + || compare()(key, front().first) + || compare()(back().first, key)) { + return end(); + } + auto where = getLowerBound(key); + return compare()(key, where->first) ? impl().end() : where; + } + + template + const_iterator findFirst(const OtherKey &key) const { + if (empty() + || compare()(key, front().first) + || compare()(back().first, key)) { + return end(); + } + auto where = getLowerBound(key); + return compare()(key, where->first) ? impl().end() : where; + } + bool contains(const Key &key) const { return findFirst(key) != end(); } @@ -558,48 +587,54 @@ private: return _data.elements; } - typename impl_t::iterator getLowerBound(const Key &key) { + template + typename impl_t::iterator getLowerBound(const OtherKey &key) { return std::lower_bound( std::begin(impl()), std::end(impl()), key, compare()); } - typename impl_t::const_iterator getLowerBound(const Key &key) const { + template + typename impl_t::const_iterator getLowerBound(const OtherKey &key) const { return std::lower_bound( std::begin(impl()), std::end(impl()), key, compare()); } - typename impl_t::iterator getUpperBound(const Key &key) { + template + typename impl_t::iterator getUpperBound(const OtherKey &key) { return std::upper_bound( std::begin(impl()), std::end(impl()), key, compare()); } - typename impl_t::const_iterator getUpperBound(const Key &key) const { + template + typename impl_t::const_iterator getUpperBound(const OtherKey &key) const { return std::upper_bound( std::begin(impl()), std::end(impl()), key, compare()); } + template std::pair< typename impl_t::iterator, typename impl_t::iterator - > getEqualRange(const Key &key) { + > getEqualRange(const OtherKey &key) { return std::equal_range( std::begin(impl()), std::end(impl()), key, compare()); } + template std::pair< typename impl_t::const_iterator, typename impl_t::const_iterator - > getEqualRange(const Key &key) const { + > getEqualRange(const OtherKey &key) const { return std::equal_range( std::begin(impl()), std::end(impl()), @@ -720,12 +755,12 @@ public: where->second = std::move(value); return { where, false }; } - template + template std::pair emplace( - const Key &key, + OtherKey &&key, Args&&... args) { return this->insert(value_type( - key, + std::forward(key), Type(std::forward(args)...))); } template @@ -775,6 +810,14 @@ public: const_iterator find(const Key &key) const { return this->findFirst(key); } + template + iterator find(const OtherKey &key) { + return this->findFirst(key); + } + template + const_iterator find(const OtherKey &key) const { + return this->findFirst(key); + } Type &operator[](const Key &key) { if (this->empty() || this->compare()(key, this->front().first)) { diff --git a/Telegram/SourceFiles/base/flat_map_tests.cpp b/Telegram/SourceFiles/base/flat_map_tests.cpp index 7dad0139d..e1372c4ac 100644 --- a/Telegram/SourceFiles/base/flat_map_tests.cpp +++ b/Telegram/SourceFiles/base/flat_map_tests.cpp @@ -69,10 +69,10 @@ TEST_CASE("simple flat_maps tests", "[flat_map]") { TEST_CASE("flat_maps custom comparator", "[flat_map]") { base::flat_map v; - v.emplace({ 0 }, "a"); - v.emplace({ 5 }, "b"); - v.emplace({ 4 }, "d"); - v.emplace({ 2 }, "e"); + v.emplace(int_wrap{ 0 }, "a"); + v.emplace(int_wrap{ 5 }, "b"); + v.emplace(int_wrap{ 4 }, "d"); + v.emplace(int_wrap{ 2 }, "e"); auto checkSorted = [&] { auto prev = v.begin(); @@ -85,7 +85,7 @@ TEST_CASE("flat_maps custom comparator", "[flat_map]") { checkSorted(); SECTION("adding item puts it in the right position") { - v.emplace({ 3 }, "c"); + v.emplace(int_wrap{ 3 }, "c"); REQUIRE(v.size() == 5); REQUIRE(v.find({ 3 }) != v.end()); checkSorted(); diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 9911c4dd8..3f721a06d 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -36,44 +36,6 @@ inline constexpr D up_cast(T object) { } } -// We need a custom comparator for std::set>::find to work with pointers. -// thanks to http://stackoverflow.com/questions/18939882/raw-pointer-lookup-for-sets-of-unique-ptrs -template -struct pointer_comparator { - using is_transparent = std::true_type; - - // helper does some magic in order to reduce the number of - // pairs of types we need to know how to compare: it turns - // everything into a pointer, and then uses `std::less` - // to do the comparison: - struct helper { - T *ptr = nullptr; - helper() = default; - helper(const helper &other) = default; - helper(T *p) : ptr(p) { - } - template - helper(const std::shared_ptr &other) : ptr(other.get()) { - } - template - helper(const std::unique_ptr &other) : ptr(other.get()) { - } - bool operator<(helper other) const { - return std::less()(ptr, other.ptr); - } - }; - - // without helper, we'd need 2^n different overloads, where - // n is the number of types we want to support (so, 8 with - // raw pointers, unique pointers, and shared pointers). That - // seems silly. - // && helps enforce rvalue use only - bool operator()(const helper &&lhs, const helper &&rhs) const { - return lhs < rhs; - } - -}; - template using set_of_unique_ptr = std::set, base::pointer_comparator>; diff --git a/Telegram/SourceFiles/lottie/lottie_animation.h b/Telegram/SourceFiles/lottie/lottie_animation.h index 21e64288b..0012d5acc 100644 --- a/Telegram/SourceFiles/lottie/lottie_animation.h +++ b/Telegram/SourceFiles/lottie/lottie_animation.h @@ -10,12 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_common.h" #include "base/weak_ptr.h" -#include -#include -#include -#include -#include - class QImage; class QString; class QByteArray; @@ -28,7 +22,6 @@ namespace Lottie { class Player; class SharedState; -class FrameRenderer; QImage ReadThumbnail(const QByteArray &content); diff --git a/Telegram/SourceFiles/lottie/lottie_common.h b/Telegram/SourceFiles/lottie/lottie_common.h index fc6d3f141..9c2c7b3b1 100644 --- a/Telegram/SourceFiles/lottie/lottie_common.h +++ b/Telegram/SourceFiles/lottie/lottie_common.h @@ -27,16 +27,6 @@ struct Information { QSize size; }; -struct DisplayFrameRequest { - crl::time time = 0; -}; - -struct Update { - base::variant< - Information, - DisplayFrameRequest> data; -}; - enum class Error { ParseFailed, NotSupported, diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp index 885da6944..fa23c93dd 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.cpp @@ -49,7 +49,7 @@ public: crl::weak_on_queue weak); void append(std::unique_ptr entry); - void frameShown(not_null entry); + void frameShown(); void updateFrameRequest( not_null entry, const FrameRequest &request); @@ -140,7 +140,7 @@ void FrameRendererObject::append(std::unique_ptr state) { queueGenerateFrames(); } -void FrameRendererObject::frameShown(not_null entry) { +void FrameRendererObject::frameShown() { queueGenerateFrames(); } @@ -423,28 +423,29 @@ crl::time SharedState::nextFrameDisplayTime() const { Unexpected("Counter value in VideoTrack::Shared::nextFrameDisplayTime."); } -crl::time SharedState::markFrameDisplayed(crl::time now) { +crl::time SharedState::markFrameDisplayed(crl::time now, crl::time delayed) { const auto mark = [&](int counter) { const auto next = (counter + 1) % (2 * kFramesCount); const auto index = next / 2; const auto frame = getFrame(index); Assert(frame->position != kTimeUnknown); - Assert(frame->displayed == kTimeUnknown); + if (frame->displayed != kTimeUnknown) { + return kTimeUnknown; + } frame->displayed = now; - _accumulatedDelayMs += (frame->displayed - frame->display); - return frame->position; }; + _accumulatedDelayMs += delayed; switch (counter()) { - case 0: Unexpected("Value 0 in SharedState::markFrameDisplayed."); + case 0: return kTimeUnknown; case 1: return mark(1); - case 2: Unexpected("Value 2 in SharedState::markFrameDisplayed."); + case 2: return kTimeUnknown; case 3: return mark(3); - case 4: Unexpected("Value 4 in SharedState::markFrameDisplayed."); + case 4: return kTimeUnknown; case 5: return mark(5); - case 6: Unexpected("Value 6 in SharedState::markFrameDisplayed."); + case 6: return kTimeUnknown; case 7: return mark(7); } Unexpected("Counter value in Lottie::SharedState::markFrameDisplayed."); @@ -480,11 +481,15 @@ crl::time SharedState::markFrameShown() { SharedState::~SharedState() = default; +std::shared_ptr FrameRenderer::CreateIndependent() { + return std::make_shared(); +} + std::shared_ptr FrameRenderer::Instance() { if (auto result = GlobalInstance.lock()) { return result; } - auto result = std::make_shared(); + auto result = CreateIndependent(); GlobalInstance = result; return result; } @@ -496,9 +501,9 @@ void FrameRenderer::append(std::unique_ptr entry) { }); } -void FrameRenderer::frameShown(not_null entry) { +void FrameRenderer::frameShown() { _wrapped.with([=](FrameRendererObject &unwrapped) { - unwrapped.frameShown(entry); + unwrapped.frameShown(); }); } diff --git a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h index 81343b30c..79ae0a521 100644 --- a/Telegram/SourceFiles/lottie/lottie_frame_renderer.h +++ b/Telegram/SourceFiles/lottie/lottie_frame_renderer.h @@ -62,7 +62,7 @@ public: [[nodiscard]] not_null frameForPaint(); [[nodiscard]] crl::time nextFrameDisplayTime() const; - crl::time markFrameDisplayed(crl::time now); + crl::time markFrameDisplayed(crl::time now, crl::time delayed); crl::time markFrameShown(); void renderFrame(QImage &image, const FrameRequest &request, int index); @@ -108,6 +108,7 @@ class FrameRendererObject; class FrameRenderer final { public: + static std::shared_ptr CreateIndependent(); static std::shared_ptr Instance(); void append(std::unique_ptr entry); @@ -115,7 +116,7 @@ public: void updateFrameRequest( not_null entry, const FrameRequest &request); - void frameShown(not_null entry); + void frameShown(); void remove(not_null state); private: diff --git a/Telegram/SourceFiles/lottie/lottie_multi_player.cpp b/Telegram/SourceFiles/lottie/lottie_multi_player.cpp new file mode 100644 index 000000000..badb8c789 --- /dev/null +++ b/Telegram/SourceFiles/lottie/lottie_multi_player.cpp @@ -0,0 +1,203 @@ +/* +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 "lottie/lottie_multi_player.h" + +#include "lottie/lottie_frame_renderer.h" +#include "lottie/lottie_animation.h" + +#include + +namespace Lottie { + +std::shared_ptr MakeFrameRenderer() { + return FrameRenderer::CreateIndependent(); +} + +MultiPlayer::MultiPlayer(std::shared_ptr renderer) +: _renderer(renderer ? std::move(renderer) : FrameRenderer::Instance()) { +} + +MultiPlayer::~MultiPlayer() { + for (const auto &[animation, state] : _active) { + _renderer->remove(state); + } +} + +not_null MultiPlayer::append( + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. + const QByteArray &content, + const FrameRequest &request) { + _animations.push_back(std::make_unique( + this, + std::move(get), + std::move(put), + content, + request)); + return _animations.back().get(); +} + +not_null MultiPlayer::append( + const QByteArray &content, + const FrameRequest &request) { + _animations.push_back(std::make_unique( + this, + content, + request)); + return _animations.back().get(); +} + +crl::time MultiPlayer::startAtRightTime(not_null state) { + Expects(!_active.empty()); + Expects((_active.size() == 1) == (_started == kTimeUnknown)); + + if (_active.size() == 1) { + _started = crl::now(); + state->start(this, _started); + return _started; + } + + const auto now = crl::now(); + const auto rate = state->information().frameRate; + Assert(rate != 0); + + const auto started = _started + _accumulatedDelay; + const auto skipFrames = (now - started) * rate / 1000; + const auto startAt = started + (1000 * skipFrames / rate); + + state->start(this, startAt); + return startAt; +} + +void MultiPlayer::start( + not_null animation, + std::unique_ptr state) { + Expects(state != nullptr); + + _active.emplace(animation, state.get()); + + auto information = state->information(); + startAtRightTime(state.get()); + _renderer->append(std::move(state)); + _updates.fire({}); + + crl::on_main_update_requests( + ) | rpl::start_with_next([=] { + checkStep(); + }, _lifetime); + + _nextFrameTime = kTimeUnknown; + _timer.cancel(); + checkStep(); +} + +void MultiPlayer::remove(not_null animation) { + const auto i = _active.find(animation); + if (i != end(_active)) { + _renderer->remove(i->second); + } + _animations.erase( + ranges::remove( + _animations, + animation.get(), + &std::unique_ptr::get), + end(_animations)); + + if (_active.empty()) { + _started = kTimeUnknown; + _accumulatedDelay = 0; + _nextFrameTime = kTimeUnknown; + _timer.cancel(); + } +} + +void MultiPlayer::failed(not_null animation, Error error) { + //_updates.fire({ animation, error }); +} + +rpl::producer MultiPlayer::updates() const { + return _updates.events(); +} + +void MultiPlayer::checkStep() { + if (_nextFrameTime != kTimeUnknown) { + checkNextFrameRender(); + } else { + checkNextFrameAvailability(); + } +} + +void MultiPlayer::checkNextFrameAvailability() { + if (_active.empty()) { + return; + } + auto next = kTimeUnknown; + for (const auto &[animation, state] : _active) { + const auto time = state->nextFrameDisplayTime(); + if (time == kTimeUnknown) { + return; + } + if (next == kTimeUnknown || next > time) { + next = time; + } + } + Assert(next != kTimeUnknown); + _nextFrameTime = next; + checkNextFrameRender(); +} + +void MultiPlayer::checkNextFrameRender() { + Expects(_nextFrameTime != kTimeUnknown); + + const auto now = crl::now(); + if (now < _nextFrameTime) { + if (!_timer.isActive()) { + _timer.callOnce(_nextFrameTime - now); + } + } else { + _timer.cancel(); + + const auto exact = std::exchange(_nextFrameTime, kTimeUnknown); + markFrameDisplayed(now, now - exact); + _updates.fire({}); + checkStep(); + } +} + +void MultiPlayer::updateFrameRequest( + not_null animation, + const FrameRequest &request) { + const auto i = _active.find(animation.get()); + Assert(i != _active.end()); + + _renderer->updateFrameRequest(i->second, request); +} + +void MultiPlayer::markFrameDisplayed(crl::time now, crl::time delayed) { + Expects(!_active.empty()); + + for (const auto &[animation, state] : _active) { + const auto time = state->nextFrameDisplayTime(); + Assert(time != kTimeUnknown); + if (now >= time) { + state->markFrameDisplayed(now, delayed); + } + } +} + +void MultiPlayer::markFrameShown() { + if (_active.empty()) { + return; + } + for (const auto &[animation, state] : _active) { + state->markFrameShown(); + } + _renderer->frameShown(); +} + +} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_multi_player.h b/Telegram/SourceFiles/lottie/lottie_multi_player.h new file mode 100644 index 000000000..6a19a34a8 --- /dev/null +++ b/Telegram/SourceFiles/lottie/lottie_multi_player.h @@ -0,0 +1,77 @@ +/* +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 "lottie/lottie_player.h" +#include "base/timer.h" +#include "base/algorithm.h" + +#include + +namespace Lottie { + +class Animation; +class FrameRenderer; + +struct MultiUpdate { + //base::variant< + // std::pair, + // DisplayMultiFrameRequest, + // std::pair> data; +}; + +std::shared_ptr MakeFrameRenderer(); + +class MultiPlayer final : public Player { +public: + explicit MultiPlayer(std::shared_ptr renderer = nullptr); + ~MultiPlayer(); + + void start( + not_null animation, + std::unique_ptr state) override; + void failed(not_null animation, Error error) override; + void updateFrameRequest( + not_null animation, + const FrameRequest &request) override; + void markFrameShown() override; + void checkStep() override; + + not_null append( + FnMut)> get, // Main thread. + FnMut put, // Unknown thread. + const QByteArray &content, + const FrameRequest &request); + not_null append( + const QByteArray &content, + const FrameRequest &request); + + rpl::producer updates() const; + + void remove(not_null animation); + +private: + crl::time startAtRightTime(not_null state); + void markFrameDisplayed(crl::time now, crl::time delayed); + void checkNextFrameAvailability(); + void checkNextFrameRender(); + + base::Timer _timer; + const std::shared_ptr _renderer; + std::vector> _animations; + base::flat_map, not_null> _active; + //base::flat_map, not_null> _paused; + crl::time _started = kTimeUnknown; + crl::time _accumulatedDelay = 0; + crl::time _nextFrameTime = kTimeUnknown; + rpl::event_stream _updates; + rpl::lifetime _lifetime; + +}; + +} // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_player.h b/Telegram/SourceFiles/lottie/lottie_player.h index 47b5aaad4..5aaa87965 100644 --- a/Telegram/SourceFiles/lottie/lottie_player.h +++ b/Telegram/SourceFiles/lottie/lottie_player.h @@ -22,17 +22,10 @@ public: not_null animation, std::unique_ptr state) = 0; virtual void failed(not_null animation, Error error) = 0; - - [[nodiscard]] virtual rpl::producer updates() = 0; - virtual void updateFrameRequest( not_null animation, const FrameRequest &request) = 0; - - // Returns frame position, if any frame was marked as displayed. - virtual crl::time markFrameDisplayed(crl::time now) = 0; - virtual crl::time markFrameShown() = 0; - + virtual void markFrameShown() = 0; virtual void checkStep() = 0; virtual ~Player() = default; diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.cpp b/Telegram/SourceFiles/lottie/lottie_single_player.cpp index a618cc67a..7d2edde49 100644 --- a/Telegram/SourceFiles/lottie/lottie_single_player.cpp +++ b/Telegram/SourceFiles/lottie/lottie_single_player.cpp @@ -10,9 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_frame_renderer.h" namespace Lottie { -namespace { - -} // namespace SinglePlayer::SinglePlayer( const QByteArray &content, @@ -32,6 +29,12 @@ SinglePlayer::SinglePlayer( , _renderer(FrameRenderer::Instance()) { } +SinglePlayer::~SinglePlayer() { + if (_state) { + _renderer->remove(_state); + } +} + void SinglePlayer::start( not_null animation, std::unique_ptr state) { @@ -40,7 +43,6 @@ void SinglePlayer::start( _state = state.get(); auto information = state->information(); state->start(this, crl::now()); - _renderer = FrameRenderer::Instance(); _renderer->append(std::move(state)); _updates.fire({ std::move(information) }); @@ -56,13 +58,7 @@ void SinglePlayer::failed(not_null animation, Error error) { _updates.fire_error(std::move(error)); } -SinglePlayer::~SinglePlayer() { - if (_state) { - _renderer->remove(_state); - } -} - -rpl::producer SinglePlayer::updates() { +rpl::producer SinglePlayer::updates() const { return _updates.events(); } @@ -87,7 +83,7 @@ void SinglePlayer::checkNextFrameAvailability() { _nextFrameTime = _state->nextFrameDisplayTime(); if (_nextFrameTime != kTimeUnknown) { - checkStep(); + checkNextFrameRender(); } } @@ -102,8 +98,8 @@ void SinglePlayer::checkNextFrameRender() { } else { _timer.cancel(); - _nextFrameTime = kTimeUnknown; - const auto position = markFrameDisplayed(now); + const auto exact = std::exchange(_nextFrameTime, kTimeUnknown); + const auto position = _state->markFrameDisplayed(now, now - exact); _updates.fire({ DisplayFrameRequest{ position } }); } } @@ -117,19 +113,11 @@ void SinglePlayer::updateFrameRequest( _renderer->updateFrameRequest(_state, request); } -crl::time SinglePlayer::markFrameDisplayed(crl::time now) { +void SinglePlayer::markFrameShown() { Expects(_state != nullptr); - return _state->markFrameDisplayed(now); -} - -crl::time SinglePlayer::markFrameShown() { - Expects(_renderer != nullptr); - - const auto result = _state->markFrameShown(); - _renderer->frameShown(_state); - - return result; + _state->markFrameShown(); + _renderer->frameShown(); } } // namespace Lottie diff --git a/Telegram/SourceFiles/lottie/lottie_single_player.h b/Telegram/SourceFiles/lottie/lottie_single_player.h index efc6adbc8..fe4f41983 100644 --- a/Telegram/SourceFiles/lottie/lottie_single_player.h +++ b/Telegram/SourceFiles/lottie/lottie_single_player.h @@ -11,8 +11,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_animation.h" #include "base/timer.h" +#include + namespace Lottie { +class FrameRenderer; + +struct DisplayFrameRequest { + crl::time time = 0; +}; + +struct Update { + base::variant< + Information, + DisplayFrameRequest> data; +}; + class SinglePlayer final : public Player { public: SinglePlayer( @@ -29,29 +43,24 @@ public: not_null animation, std::unique_ptr state) override; void failed(not_null animation, Error error) override; - - rpl::producer updates() override; - - [[nodiscard]] bool ready() const; - [[nodiscard]] QImage frame(const FrameRequest &request) const; - void updateFrameRequest( not_null animation, const FrameRequest &request) override; - - // Returns frame position, if any frame was marked as displayed. - crl::time markFrameDisplayed(crl::time now) override; - crl::time markFrameShown() override; - + void markFrameShown() override; void checkStep() override; + rpl::producer updates() const; + + [[nodiscard]] bool ready() const; + [[nodiscard]] QImage frame(const FrameRequest &request) const; + private: void checkNextFrameAvailability(); void checkNextFrameRender(); Animation _animation; base::Timer _timer; - std::shared_ptr _renderer; + const std::shared_ptr _renderer; SharedState *_state = nullptr; crl::time _nextFrameTime = kTimeUnknown; rpl::event_stream _updates; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 56fe9f0e8..a39b18aff 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -111,7 +111,7 @@ void Player::checkNextFrameAvailability() { _nextFrameTime = _video->nextFrameDisplayTime(); if (_nextFrameTime != kTimeUnknown) { - checkVideoStep(); + checkNextFrameRender(); } } diff --git a/Telegram/gyp/lib_lottie.gyp b/Telegram/gyp/lib_lottie.gyp index ef9b2b9cf..ada149e85 100644 --- a/Telegram/gyp/lib_lottie.gyp +++ b/Telegram/gyp/lib_lottie.gyp @@ -64,6 +64,8 @@ '<(src_loc)/lottie/lottie_common.h', '<(src_loc)/lottie/lottie_frame_renderer.cpp', '<(src_loc)/lottie/lottie_frame_renderer.h', + '<(src_loc)/lottie/lottie_multi_player.cpp', + '<(src_loc)/lottie/lottie_multi_player.h', '<(src_loc)/lottie/lottie_player.h', '<(src_loc)/lottie/lottie_single_player.cpp', '<(src_loc)/lottie/lottie_single_player.h',