media_streaming_document.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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 "media/streaming/media_streaming_document.h"
  8. #include "media/streaming/media_streaming_instance.h"
  9. #include "media/streaming/media_streaming_loader.h"
  10. #include "media/streaming/media_streaming_reader.h"
  11. #include "data/data_session.h"
  12. #include "data/data_document.h"
  13. #include "data/data_photo.h"
  14. #include "data/data_document_media.h"
  15. #include "data/data_file_origin.h"
  16. #include "main/main_session.h"
  17. #include "storage/file_download.h" // Storage::kMaxFileInMemory.
  18. #include "styles/style_widgets.h"
  19. #include <QtCore/QBuffer>
  20. namespace Media {
  21. namespace Streaming {
  22. namespace {
  23. constexpr auto kWaitingFastDuration = crl::time(200);
  24. constexpr auto kWaitingShowDuration = crl::time(500);
  25. constexpr auto kWaitingShowDelay = crl::time(500);
  26. constexpr auto kGoodThumbQuality = 87;
  27. constexpr auto kSwitchQualityUpPreloadedThreshold = 4 * crl::time(1000);
  28. constexpr auto kSwitchQualityUpSpeedMultiplier = 1.2;
  29. } // namespace
  30. Document::Document(
  31. not_null<DocumentData*> document,
  32. std::shared_ptr<Reader> reader,
  33. std::vector<QualityDescriptor> otherQualities)
  34. : Document(std::move(reader), document, {}, std::move(otherQualities)) {
  35. _player.fullInCache(
  36. ) | rpl::start_with_next([=](bool fullInCache) {
  37. _document->setLoadedInMediaCache(fullInCache);
  38. }, _player.lifetime());
  39. }
  40. Document::Document(
  41. not_null<PhotoData*> photo,
  42. std::shared_ptr<Reader> reader,
  43. std::vector<QualityDescriptor> otherQualities)
  44. : Document(std::move(reader), {}, photo, {}) {
  45. }
  46. Document::Document(std::unique_ptr<Loader> loader)
  47. : Document(std::make_shared<Reader>(std::move(loader)), {}, {}, {}) {
  48. }
  49. Document::Document(
  50. std::shared_ptr<Reader> reader,
  51. DocumentData *document,
  52. PhotoData *photo,
  53. std::vector<QualityDescriptor> otherQualities)
  54. : _document(document)
  55. , _photo(photo)
  56. , _player(std::move(reader))
  57. , _radial(
  58. [=] { waitingCallback(); },
  59. st::defaultInfiniteRadialAnimation)
  60. , _otherQualities(std::move(otherQualities)) {
  61. resubscribe();
  62. }
  63. void Document::resubscribe() {
  64. _subscription = _player.updates(
  65. ) | rpl::start_with_next_error([=](Update &&update) {
  66. handleUpdate(std::move(update));
  67. }, [=](Streaming::Error &&error) {
  68. handleError(std::move(error));
  69. resubscribe();
  70. });
  71. }
  72. Player &Document::player() {
  73. return _player;
  74. }
  75. const Player &Document::player() const {
  76. return _player;
  77. }
  78. const Information &Document::info() const {
  79. return _info;
  80. }
  81. void Document::play(const PlaybackOptions &options) {
  82. _player.play(options);
  83. _info.audio.state.position
  84. = _info.video.state.position
  85. = options.position;
  86. waitingChange(true);
  87. }
  88. void Document::saveFrameToCover() {
  89. _info.video.cover = _player.ready()
  90. ? _player.currentFrameImage()
  91. : _info.video.cover;
  92. }
  93. void Document::registerInstance(not_null<Instance*> instance) {
  94. _instances.emplace(instance);
  95. }
  96. void Document::unregisterInstance(not_null<Instance*> instance) {
  97. _instances.remove(instance);
  98. _player.unregisterInstance(instance);
  99. refreshPlayerPriority();
  100. }
  101. void Document::refreshPlayerPriority() {
  102. if (_instances.empty()) {
  103. return;
  104. }
  105. const auto max = ranges::max_element(
  106. _instances,
  107. ranges::less(),
  108. &Instance::priority);
  109. _player.setLoaderPriority((*max)->priority());
  110. }
  111. bool Document::waitingShown() const {
  112. if (!_fading.animating() && !_waiting) {
  113. _radial.stop(anim::type::instant);
  114. return false;
  115. }
  116. return _radial.animating();
  117. }
  118. float64 Document::waitingOpacity() const {
  119. return _fading.value(_waiting ? 1. : 0.);
  120. }
  121. Ui::RadialState Document::waitingState() const {
  122. return _radial.computeState();
  123. }
  124. rpl::producer<int> Document::switchQualityRequests() const {
  125. return _switchQualityRequests.events();
  126. }
  127. void Document::handleUpdate(Update &&update) {
  128. v::match(update.data, [&](Information &update) {
  129. ready(std::move(update));
  130. }, [&](PreloadedVideo update) {
  131. _info.video.state.receivedTill = update.till;
  132. checkSwitchToHigherQuality();
  133. }, [&](UpdateVideo update) {
  134. _info.video.state.position = update.position;
  135. }, [&](PreloadedAudio update) {
  136. _info.audio.state.receivedTill = update.till;
  137. }, [&](UpdateAudio update) {
  138. _info.audio.state.position = update.position;
  139. }, [&](WaitingForData update) {
  140. waitingChange(update.waiting);
  141. }, [&](SpeedEstimate update) {
  142. checkForQualitySwitch(update);
  143. }, [](MutedByOther) {
  144. }, [&](Finished) {
  145. const auto finishTrack = [](TrackState &state) {
  146. state.position = state.receivedTill = state.duration;
  147. };
  148. finishTrack(_info.audio.state);
  149. finishTrack(_info.video.state);
  150. });
  151. }
  152. void Document::setOtherQualities(std::vector<QualityDescriptor> value) {
  153. _otherQualities = std::move(value);
  154. checkForQualitySwitch(_lastSpeedEstimate);
  155. }
  156. void Document::checkForQualitySwitch(SpeedEstimate estimate) {
  157. _lastSpeedEstimate = estimate;
  158. if (!checkSwitchToHigherQuality()) {
  159. checkSwitchToLowerQuality();
  160. }
  161. }
  162. bool Document::checkSwitchToHigherQuality() {
  163. if (_otherQualities.empty()
  164. || (_info.video.state.duration == kTimeUnknown)
  165. || (_info.video.state.duration == kDurationUnavailable)
  166. || (_info.video.state.position == kTimeUnknown)
  167. || (_info.video.state.receivedTill == kTimeUnknown)
  168. || !_lastSpeedEstimate.bytesPerSecond
  169. || _lastSpeedEstimate.unreliable
  170. || (_info.video.state.receivedTill
  171. < std::min(
  172. _info.video.state.duration,
  173. (_info.video.state.position
  174. + kSwitchQualityUpPreloadedThreshold)))) {
  175. return false;
  176. }
  177. const auto size = _player.fileSize();
  178. Assert(size >= 0 && size <= std::numeric_limits<uint32>::max());
  179. auto to = QualityDescriptor{ .sizeInBytes = uint32(size) };
  180. const auto duration = _info.video.state.duration / 1000.;
  181. const auto speed = _player.speed();
  182. const auto multiplier = speed * kSwitchQualityUpSpeedMultiplier;
  183. for (const auto &descriptor : _otherQualities) {
  184. const auto perSecond = descriptor.sizeInBytes / duration;
  185. if (descriptor.sizeInBytes > to.sizeInBytes
  186. && _lastSpeedEstimate.bytesPerSecond >= perSecond * multiplier) {
  187. to = descriptor;
  188. }
  189. }
  190. if (!to.height) {
  191. return false;
  192. }
  193. _switchQualityRequests.fire_copy(to.height);
  194. return true;
  195. }
  196. bool Document::checkSwitchToLowerQuality() {
  197. if (_otherQualities.empty()
  198. || !_waiting
  199. || !_radial.animating()
  200. || !_lastSpeedEstimate.bytesPerSecond) {
  201. return false;
  202. }
  203. const auto size = _player.fileSize();
  204. Assert(size >= 0 && size <= std::numeric_limits<uint32>::max());
  205. auto to = QualityDescriptor();
  206. for (const auto &descriptor : _otherQualities) {
  207. if (descriptor.sizeInBytes < size
  208. && descriptor.sizeInBytes > to.sizeInBytes) {
  209. to = descriptor;
  210. }
  211. }
  212. if (!to.height) {
  213. return false;
  214. }
  215. _switchQualityRequests.fire_copy(to.height);
  216. return true;
  217. }
  218. void Document::handleError(Error &&error) {
  219. if (_document) {
  220. if (error == Error::NotStreamable) {
  221. _document->setNotSupportsStreaming();
  222. } else if (error == Error::OpenFailed) {
  223. _document->setInappPlaybackFailed();
  224. }
  225. } else if (_photo) {
  226. if (error == Error::NotStreamable || error == Error::OpenFailed) {
  227. _photo->setVideoPlaybackFailed();
  228. }
  229. }
  230. waitingChange(false);
  231. }
  232. void Document::ready(Information &&info) {
  233. _info = std::move(info);
  234. validateGoodThumbnail();
  235. waitingChange(false);
  236. }
  237. void Document::waitingChange(bool waiting) {
  238. if (_waiting == waiting) {
  239. return;
  240. }
  241. _waiting = waiting;
  242. const auto fade = [=](crl::time duration) {
  243. if (!_radial.animating()) {
  244. _radial.start(
  245. st::defaultInfiniteRadialAnimation.sineDuration);
  246. }
  247. _fading.start([=] {
  248. waitingCallback();
  249. }, _waiting ? 0. : 1., _waiting ? 1. : 0., duration);
  250. checkSwitchToLowerQuality();
  251. };
  252. if (waiting) {
  253. if (_radial.animating()) {
  254. _timer.cancel();
  255. fade(kWaitingFastDuration);
  256. } else {
  257. _timer.callOnce(kWaitingShowDelay);
  258. _timer.setCallback([=] {
  259. fade(kWaitingShowDuration);
  260. });
  261. }
  262. } else {
  263. _timer.cancel();
  264. if (_radial.animating()) {
  265. fade(kWaitingFastDuration);
  266. }
  267. }
  268. }
  269. void Document::validateGoodThumbnail() {
  270. if (_info.video.cover.isNull()
  271. || !_document
  272. || _document->goodThumbnailChecked()) {
  273. return;
  274. }
  275. const auto sticker = (_document->sticker() != nullptr);
  276. const auto document = _document;
  277. const auto information = _info.video;
  278. const auto key = document->goodThumbnailCacheKey();
  279. const auto guard = base::make_weak(&document->session());
  280. document->owner().cache().get(key, [=](QByteArray value) {
  281. if (!value.isEmpty()) {
  282. return;
  283. }
  284. const auto image = [&] {
  285. auto result = information.cover;
  286. if (information.rotation != 0) {
  287. auto transform = QTransform();
  288. transform.rotate(information.rotation);
  289. result = result.transformed(transform);
  290. }
  291. if (result.size() != information.size) {
  292. result = result.scaled(
  293. information.size,
  294. Qt::IgnoreAspectRatio,
  295. Qt::SmoothTransformation);
  296. }
  297. if (!sticker && information.alpha) {
  298. result = Images::Opaque(std::move(result));
  299. }
  300. return result;
  301. }();
  302. auto bytes = QByteArray();
  303. {
  304. auto buffer = QBuffer(&bytes);
  305. image.save(&buffer, sticker ? "WEBP" : "JPG", kGoodThumbQuality);
  306. }
  307. const auto length = bytes.size();
  308. if (!length || length > Storage::kMaxFileInMemory) {
  309. LOG(("App Error: Bad thumbnail data for saving to cache."));
  310. bytes = "(failed)"_q;
  311. }
  312. crl::on_main(guard, [=] {
  313. if (const auto active = document->activeMediaView()) {
  314. active->setGoodThumbnail(image);
  315. }
  316. if (bytes != "(failed)"_q) {
  317. document->setGoodThumbnailChecked(true);
  318. }
  319. document->owner().cache().putIfEmpty(
  320. document->goodThumbnailCacheKey(),
  321. Storage::Cache::Database::TaggedValue(
  322. base::duplicate(bytes),
  323. Data::kImageCacheTag));
  324. });
  325. });
  326. }
  327. void Document::waitingCallback() {
  328. for (const auto &instance : _instances) {
  329. instance->callWaitingCallback();
  330. }
  331. }
  332. } // namespace Streaming
  333. } // namespace Media