lottie_multi_player.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "lottie/lottie_multi_player.h"
  8. #include "lottie/details/lottie_frame_renderer.h"
  9. #include "lottie/lottie_animation.h"
  10. #include <range/v3/algorithm/remove.hpp>
  11. namespace Lottie {
  12. MultiPlayer::MultiPlayer(
  13. Quality quality,
  14. std::shared_ptr<FrameRenderer> renderer)
  15. : _quality(quality)
  16. , _timer([=] { checkNextFrameRender(); })
  17. , _renderer(renderer ? std::move(renderer) : FrameRenderer::Instance()) {
  18. crl::on_main_update_requests(
  19. ) | rpl::start_with_next([=] {
  20. checkStep();
  21. }, _lifetime);
  22. }
  23. MultiPlayer::~MultiPlayer() {
  24. for (const auto &[animation, state] : _active) {
  25. _renderer->remove(state);
  26. }
  27. for (const auto &[animation, info] : _paused) {
  28. _renderer->remove(info.state);
  29. }
  30. }
  31. not_null<Animation*> MultiPlayer::append(
  32. FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
  33. FnMut<void(QByteArray &&cached)> put, // Unknown thread.
  34. const QByteArray &content,
  35. const FrameRequest &request) {
  36. #ifdef LOTTIE_USE_CACHE
  37. _animations.push_back(std::make_unique<Animation>(
  38. this,
  39. std::move(get),
  40. std::move(put),
  41. content,
  42. request,
  43. _quality));
  44. return _animations.back().get();
  45. #else // LOTTIE_USE_CACHE
  46. return append(content, request);
  47. #endif // LOTTIE_USE_CACHE
  48. }
  49. not_null<Animation*> MultiPlayer::append(
  50. const QByteArray &content,
  51. const FrameRequest &request) {
  52. _animations.push_back(std::make_unique<Animation>(
  53. this,
  54. content,
  55. request,
  56. _quality));
  57. return _animations.back().get();
  58. }
  59. void MultiPlayer::startAtRightTime(std::unique_ptr<SharedState> state) {
  60. if (_started == kTimeUnknown) {
  61. _started = crl::now();
  62. _lastSyncTime = kTimeUnknown;
  63. _delay = 0;
  64. }
  65. const auto lastSyncTime = (_lastSyncTime != kTimeUnknown)
  66. ? _lastSyncTime
  67. : _started;
  68. const auto frameIndex = countFrameIndex(
  69. state.get(),
  70. lastSyncTime,
  71. _delay);
  72. state->start(this, _started, _delay, frameIndex);
  73. const auto request = state->frameForPaint()->request;
  74. _renderer->append(std::move(state), request);
  75. }
  76. int MultiPlayer::countFrameIndex(
  77. not_null<SharedState*> state,
  78. crl::time time,
  79. crl::time delay) const {
  80. Expects(time != kTimeUnknown);
  81. const auto rate = state->information().frameRate;
  82. Assert(rate != 0);
  83. const auto framesTime = time - _started - delay;
  84. return ((framesTime + 1) * rate - 1) / 1000;
  85. }
  86. void MultiPlayer::start(
  87. not_null<Animation*> animation,
  88. std::unique_ptr<SharedState> state) {
  89. Expects(state != nullptr);
  90. const auto paused = _pausedBeforeStart.remove(animation);
  91. auto info = StartingInfo{ std::move(state), paused };
  92. if (_active.empty()
  93. || (_lastSyncTime == kTimeUnknown
  94. && _nextFrameTime == kTimeUnknown)) {
  95. addNewToActive(animation, std::move(info));
  96. } else {
  97. // We always try to mark as shown at the same time, so we start a new
  98. // animation at the same time we mark all existing as shown.
  99. _pendingToStart.emplace(animation, std::move(info));
  100. }
  101. _updates.fire({});
  102. }
  103. void MultiPlayer::addNewToActive(
  104. not_null<Animation*> animation,
  105. StartingInfo &&info) {
  106. _active.emplace(animation, info.state.get());
  107. startAtRightTime(std::move(info.state));
  108. if (info.paused) {
  109. _pendingPause.emplace(animation);
  110. }
  111. }
  112. void MultiPlayer::processPending() {
  113. Expects(_lastSyncTime != kTimeUnknown);
  114. for (const auto &animation : base::take(_pendingPause)) {
  115. pauseAndSaveState(animation);
  116. }
  117. for (const auto &animation : base::take(_pendingUnpause)) {
  118. unpauseAndKeepUp(animation);
  119. }
  120. for (auto &[animation, info] : base::take(_pendingToStart)) {
  121. addNewToActive(animation, std::move(info));
  122. }
  123. for (const auto &animation : base::take(_pendingRemove)) {
  124. removeNow(animation);
  125. }
  126. }
  127. void MultiPlayer::remove(not_null<Animation*> animation) {
  128. if (!_active.empty()) {
  129. _pendingRemove.emplace(animation);
  130. } else {
  131. removeNow(animation);
  132. }
  133. }
  134. void MultiPlayer::removeNow(not_null<Animation*> animation) {
  135. const auto i = _active.find(animation);
  136. if (i != end(_active)) {
  137. _renderer->remove(i->second);
  138. _active.erase(i);
  139. }
  140. const auto j = _paused.find(animation);
  141. if (j != end(_paused)) {
  142. _renderer->remove(j->second.state);
  143. _paused.erase(j);
  144. }
  145. _pendingRemove.remove(animation);
  146. _pendingToStart.remove(animation);
  147. _pendingPause.remove(animation);
  148. _pendingUnpause.remove(animation);
  149. _pausedBeforeStart.remove(animation);
  150. _animations.erase(
  151. ranges::remove(
  152. _animations,
  153. animation.get(),
  154. &std::unique_ptr<Animation>::get),
  155. end(_animations));
  156. if (_active.empty()) {
  157. _nextFrameTime = kTimeUnknown;
  158. _timer.cancel();
  159. if (_paused.empty()) {
  160. _started = kTimeUnknown;
  161. _lastSyncTime = kTimeUnknown;
  162. _delay = 0;
  163. }
  164. }
  165. }
  166. void MultiPlayer::pause(not_null<Animation*> animation) {
  167. if (_active.contains(animation)) {
  168. _pendingPause.emplace(animation);
  169. } else if (_paused.contains(animation)) {
  170. _pendingUnpause.remove(animation);
  171. } else if (const auto i = _pendingToStart.find(animation); i != end(_pendingToStart)) {
  172. i->second.paused = true;
  173. } else {
  174. _pausedBeforeStart.emplace(animation);
  175. }
  176. }
  177. void MultiPlayer::unpause(not_null<Animation*> animation) {
  178. if (const auto i = _paused.find(animation); i != end(_paused)) {
  179. if (_active.empty()) {
  180. unpauseFirst(animation, i->second.state);
  181. _paused.erase(i);
  182. } else {
  183. _pendingUnpause.emplace(animation);
  184. }
  185. } else if (_pendingPause.contains(animation)) {
  186. _pendingPause.remove(animation);
  187. } else {
  188. const auto i = _pendingToStart.find(animation);
  189. if (i != end(_pendingToStart)) {
  190. i->second.paused = false;
  191. } else {
  192. _pausedBeforeStart.remove(animation);
  193. }
  194. }
  195. }
  196. void MultiPlayer::unpauseFirst(
  197. not_null<Animation*> animation,
  198. not_null<SharedState*> state) {
  199. Expects(_lastSyncTime != kTimeUnknown);
  200. _active.emplace(animation, state);
  201. const auto now = crl::now();
  202. addTimelineDelay(now - _lastSyncTime);
  203. _lastSyncTime = now;
  204. markFrameShown();
  205. }
  206. void MultiPlayer::pauseAndSaveState(not_null<Animation*> animation) {
  207. Expects(_lastSyncTime != kTimeUnknown);
  208. const auto i = _active.find(animation);
  209. Assert(i != end(_active));
  210. _paused.emplace(
  211. animation,
  212. PausedInfo{ i->second, _lastSyncTime, _delay });
  213. _active.erase(i);
  214. }
  215. void MultiPlayer::unpauseAndKeepUp(not_null<Animation*> animation) {
  216. Expects(_lastSyncTime != kTimeUnknown);
  217. const auto i = _paused.find(animation);
  218. Assert(i != end(_paused));
  219. const auto state = i->second.state;
  220. const auto frameIndexAtPaused = countFrameIndex(
  221. state,
  222. i->second.pauseTime,
  223. i->second.pauseDelay);
  224. const auto frameIndexNow = countFrameIndex(
  225. state,
  226. _lastSyncTime,
  227. _delay);
  228. state->addTimelineDelay(
  229. (_delay - i->second.pauseDelay),
  230. frameIndexNow - frameIndexAtPaused);
  231. _active.emplace(animation, state);
  232. _paused.erase(i);
  233. }
  234. void MultiPlayer::failed(not_null<Animation*> animation, Error error) {
  235. //_updates.fire({ animation, error });
  236. }
  237. rpl::producer<MultiUpdate> MultiPlayer::updates() const {
  238. return _updates.events();
  239. }
  240. void MultiPlayer::checkStep() {
  241. if (_active.empty() || _nextFrameTime == kFrameDisplayTimeAlreadyDone) {
  242. return;
  243. } else if (_nextFrameTime != kTimeUnknown) {
  244. checkNextFrameRender();
  245. } else {
  246. checkNextFrameAvailability();
  247. }
  248. }
  249. void MultiPlayer::checkNextFrameAvailability() {
  250. Expects(_nextFrameTime == kTimeUnknown);
  251. auto next = kTimeUnknown;
  252. for (const auto &[animation, state] : _active) {
  253. const auto time = state->nextFrameDisplayTime();
  254. if (time == kTimeUnknown) {
  255. for (const auto &[animation, state] : _active) {
  256. if (state->nextFrameDisplayTime() != kTimeUnknown) {
  257. break;
  258. }
  259. }
  260. return;
  261. } else if (time == kFrameDisplayTimeAlreadyDone) {
  262. continue;
  263. }
  264. if (next == kTimeUnknown || next > time) {
  265. next = time;
  266. }
  267. }
  268. if (next == kTimeUnknown) {
  269. return;
  270. }
  271. _nextFrameTime = next;
  272. checkNextFrameRender();
  273. }
  274. void MultiPlayer::checkNextFrameRender() {
  275. Expects(_nextFrameTime != kTimeUnknown);
  276. const auto now = crl::now();
  277. if (now < _nextFrameTime) {
  278. if (!_timer.isActive()) {
  279. _timer.callOnce(_nextFrameTime - now);
  280. }
  281. } else {
  282. _timer.cancel();
  283. markFrameDisplayed(now);
  284. addTimelineDelay(now - _nextFrameTime);
  285. _lastSyncTime = now;
  286. _nextFrameTime = kFrameDisplayTimeAlreadyDone;
  287. processPending();
  288. _updates.fire({});
  289. }
  290. }
  291. void MultiPlayer::updateFrameRequest(
  292. not_null<const Animation*> animation,
  293. const FrameRequest &request) {
  294. const auto state = [&]() -> Lottie::SharedState* {
  295. if (const auto i = _active.find(animation); i != end(_active)) {
  296. return i->second;
  297. } else if (const auto j = _paused.find(animation);
  298. j != end(_paused)) {
  299. return j->second.state;
  300. } else if (const auto k = _pendingToStart.find(animation);
  301. k != end(_pendingToStart)) {
  302. return nullptr;
  303. }
  304. Unexpected("Animation in MultiPlayer::updateFrameRequest.");
  305. }();
  306. if (state) {
  307. _renderer->updateFrameRequest(state, request);
  308. }
  309. }
  310. void MultiPlayer::markFrameDisplayed(crl::time now) {
  311. Expects(!_active.empty());
  312. for (const auto &[animation, state] : _active) {
  313. const auto time = state->nextFrameDisplayTime();
  314. Assert(time != kTimeUnknown);
  315. if (time == kFrameDisplayTimeAlreadyDone) {
  316. continue;
  317. } else if (now >= time) {
  318. state->markFrameDisplayed(now);
  319. }
  320. }
  321. }
  322. void MultiPlayer::addTimelineDelay(crl::time delayed) {
  323. Expects(!_active.empty());
  324. for (const auto &[animation, state] : _active) {
  325. state->addTimelineDelay(delayed);
  326. }
  327. _delay += delayed;
  328. }
  329. bool MultiPlayer::markFrameShown() {
  330. if (_nextFrameTime == kFrameDisplayTimeAlreadyDone) {
  331. _nextFrameTime = kTimeUnknown;
  332. }
  333. auto count = 0;
  334. for (const auto &[animation, state] : _active) {
  335. if (state->markFrameShown()) {
  336. ++count;
  337. }
  338. }
  339. if (count) {
  340. _renderer->frameShown();
  341. return true;
  342. }
  343. return false;
  344. }
  345. } // namespace Lottie