data_media_preload.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "data/data_media_preload.h"
  8. #include "data/data_document.h"
  9. #include "data/data_document_media.h"
  10. #include "data/data_file_origin.h"
  11. #include "data/data_photo.h"
  12. #include "data/data_photo_media.h"
  13. #include "data/data_session.h"
  14. #include "main/main_session.h"
  15. #include "main/main_session_settings.h"
  16. #include "media/streaming/media_streaming_reader.h"
  17. #include "storage/file_download.h" // kMaxFileInMemory.
  18. namespace Data {
  19. namespace {
  20. constexpr auto kDefaultPreloadPrefix = 4 * 1024 * 1024;
  21. [[nodiscard]] int64 ChoosePreloadPrefix(not_null<DocumentData*> video) {
  22. const auto result = video->videoPreloadPrefix();
  23. return result
  24. ? result
  25. : std::min(int64(kDefaultPreloadPrefix), video->size);
  26. }
  27. } // namespace
  28. MediaPreload::MediaPreload(Fn<void()> done)
  29. : _done(std::move(done)) {
  30. }
  31. void MediaPreload::callDone() {
  32. if (const auto onstack = _done) {
  33. onstack();
  34. }
  35. }
  36. PhotoPreload::PhotoPreload(
  37. not_null<PhotoData*> photo,
  38. FileOrigin origin,
  39. Fn<void()> done)
  40. : MediaPreload(std::move(done))
  41. , _photo(photo->createMediaView()) {
  42. start(origin);
  43. }
  44. PhotoPreload::~PhotoPreload() {
  45. if (_photo) {
  46. base::take(_photo)->owner()->cancel();
  47. }
  48. }
  49. bool PhotoPreload::Should(
  50. not_null<PhotoData*> photo,
  51. not_null<PeerData*> context) {
  52. return !photo->cancelled()
  53. && AutoDownload::Should(
  54. photo->session().settings().autoDownload(),
  55. context,
  56. photo);
  57. }
  58. void PhotoPreload::start(FileOrigin origin) {
  59. if (_photo->loaded()) {
  60. callDone();
  61. } else {
  62. _photo->owner()->load(origin, LoadFromCloudOrLocal, true);
  63. _photo->owner()->session().downloaderTaskFinished(
  64. ) | rpl::filter([=] {
  65. return _photo->loaded();
  66. }) | rpl::start_with_next([=] { callDone(); }, _lifetime);
  67. }
  68. }
  69. VideoPreload::VideoPreload(
  70. not_null<DocumentData*> video,
  71. FileOrigin origin,
  72. Fn<void()> done)
  73. : MediaPreload(std::move(done))
  74. , DownloadMtprotoTask(
  75. &video->session().downloader(),
  76. video->videoPreloadLocation(),
  77. origin)
  78. , _video(video)
  79. , _full(video->size) {
  80. if (Can(video)) {
  81. check();
  82. } else {
  83. callDone();
  84. }
  85. }
  86. void VideoPreload::check() {
  87. const auto key = _video->bigFileBaseCacheKey();
  88. const auto weak = base::make_weak(static_cast<has_weak_ptr*>(this));
  89. _video->owner().cacheBigFile().get(key, [weak](
  90. const QByteArray &result) {
  91. if (!result.isEmpty()) {
  92. crl::on_main([weak] {
  93. if (const auto strong = weak.get()) {
  94. static_cast<VideoPreload*>(strong)->callDone();
  95. }
  96. });
  97. } else {
  98. crl::on_main([weak] {
  99. if (const auto strong = weak.get()) {
  100. static_cast<VideoPreload*>(strong)->load();
  101. }
  102. });
  103. }
  104. });
  105. }
  106. void VideoPreload::load() {
  107. if (!Can(_video)) {
  108. callDone();
  109. return;
  110. }
  111. const auto prefix = ChoosePreloadPrefix(_video);
  112. Assert(prefix > 0 && prefix <= _video->size);
  113. const auto part = Storage::kDownloadPartSize;
  114. const auto parts = (prefix + part - 1) / part;
  115. for (auto i = 0; i != parts; ++i) {
  116. _parts.emplace(i * part, QByteArray());
  117. }
  118. addToQueue();
  119. }
  120. void VideoPreload::done(QByteArray result) {
  121. const auto key = _video->bigFileBaseCacheKey();
  122. if (!result.isEmpty() && key) {
  123. Assert(result.size() < Storage::kMaxFileInMemory);
  124. _video->owner().cacheBigFile().putIfEmpty(
  125. key,
  126. Storage::Cache::Database::TaggedValue(std::move(result), 0));
  127. }
  128. callDone();
  129. }
  130. VideoPreload::~VideoPreload() {
  131. if (!_finished && !_failed) {
  132. cancelAllRequests();
  133. }
  134. }
  135. bool VideoPreload::Can(not_null<DocumentData*> video) {
  136. return video->canBeStreamed(nullptr)
  137. && video->videoPreloadLocation().valid()
  138. && video->bigFileBaseCacheKey();
  139. }
  140. bool VideoPreload::readyToRequest() const {
  141. const auto part = Storage::kDownloadPartSize;
  142. return !_failed && (_nextRequestOffset < _parts.size() * part);
  143. }
  144. int64 VideoPreload::takeNextRequestOffset() {
  145. Expects(readyToRequest());
  146. _requestedOffsets.emplace(_nextRequestOffset);
  147. _nextRequestOffset += Storage::kDownloadPartSize;
  148. return _requestedOffsets.back();
  149. }
  150. bool VideoPreload::feedPart(
  151. int64 offset,
  152. const QByteArray &bytes) {
  153. Expects(offset < _parts.size() * Storage::kDownloadPartSize);
  154. Expects(_requestedOffsets.contains(int(offset)));
  155. Expects(bytes.size() <= Storage::kDownloadPartSize);
  156. const auto part = Storage::kDownloadPartSize;
  157. _requestedOffsets.remove(int(offset));
  158. _parts[offset] = bytes;
  159. if ((_nextRequestOffset + part >= _parts.size() * part)
  160. && _requestedOffsets.empty()) {
  161. _finished = true;
  162. removeFromQueue();
  163. auto result = ::Media::Streaming::SerializeComplexPartsMap(_parts);
  164. if (result.size() == _full) {
  165. // Make sure it is parsed as a complex map.
  166. result.push_back(char(0));
  167. }
  168. done(std::move(result));
  169. }
  170. return true;
  171. }
  172. void VideoPreload::cancelOnFail() {
  173. _failed = true;
  174. cancelAllRequests();
  175. done({});
  176. }
  177. bool VideoPreload::setWebFileSizeHook(int64 size) {
  178. _failed = true;
  179. cancelAllRequests();
  180. done({});
  181. return false;
  182. }
  183. } // namespace Data