/* 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 "data/data_media_preload.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_file_origin.h" #include "data/data_photo.h" #include "data/data_photo_media.h" #include "data/data_session.h" #include "main/main_session.h" #include "main/main_session_settings.h" #include "media/streaming/media_streaming_reader.h" #include "storage/file_download.h" // kMaxFileInMemory. namespace Data { namespace { constexpr auto kDefaultPreloadPrefix = 4 * 1024 * 1024; [[nodiscard]] int64 ChoosePreloadPrefix(not_null video) { const auto result = video->videoPreloadPrefix(); return result ? result : std::min(int64(kDefaultPreloadPrefix), video->size); } } // namespace MediaPreload::MediaPreload(Fn done) : _done(std::move(done)) { } void MediaPreload::callDone() { if (const auto onstack = _done) { onstack(); } } PhotoPreload::PhotoPreload( not_null photo, FileOrigin origin, Fn done) : MediaPreload(std::move(done)) , _photo(photo->createMediaView()) { start(origin); } PhotoPreload::~PhotoPreload() { if (_photo) { base::take(_photo)->owner()->cancel(); } } bool PhotoPreload::Should( not_null photo, not_null context) { return !photo->cancelled() && AutoDownload::Should( photo->session().settings().autoDownload(), context, photo); } void PhotoPreload::start(FileOrigin origin) { if (_photo->loaded()) { callDone(); } else { _photo->owner()->load(origin, LoadFromCloudOrLocal, true); _photo->owner()->session().downloaderTaskFinished( ) | rpl::filter([=] { return _photo->loaded(); }) | rpl::start_with_next([=] { callDone(); }, _lifetime); } } VideoPreload::VideoPreload( not_null video, FileOrigin origin, Fn done) : MediaPreload(std::move(done)) , DownloadMtprotoTask( &video->session().downloader(), video->videoPreloadLocation(), origin) , _video(video) , _full(video->size) { if (Can(video)) { check(); } else { callDone(); } } void VideoPreload::check() { const auto key = _video->bigFileBaseCacheKey(); const auto weak = base::make_weak(static_cast(this)); _video->owner().cacheBigFile().get(key, [weak]( const QByteArray &result) { if (!result.isEmpty()) { crl::on_main([weak] { if (const auto strong = weak.get()) { static_cast(strong)->callDone(); } }); } else { crl::on_main([weak] { if (const auto strong = weak.get()) { static_cast(strong)->load(); } }); } }); } void VideoPreload::load() { if (!Can(_video)) { callDone(); return; } const auto prefix = ChoosePreloadPrefix(_video); Assert(prefix > 0 && prefix <= _video->size); const auto part = Storage::kDownloadPartSize; const auto parts = (prefix + part - 1) / part; for (auto i = 0; i != parts; ++i) { _parts.emplace(i * part, QByteArray()); } addToQueue(); } void VideoPreload::done(QByteArray result) { const auto key = _video->bigFileBaseCacheKey(); if (!result.isEmpty() && key) { Assert(result.size() < Storage::kMaxFileInMemory); _video->owner().cacheBigFile().putIfEmpty( key, Storage::Cache::Database::TaggedValue(std::move(result), 0)); } callDone(); } VideoPreload::~VideoPreload() { if (!_finished && !_failed) { cancelAllRequests(); } } bool VideoPreload::Can(not_null video) { return video->canBeStreamed(nullptr) && video->videoPreloadLocation().valid() && video->bigFileBaseCacheKey(); } bool VideoPreload::readyToRequest() const { const auto part = Storage::kDownloadPartSize; return !_failed && (_nextRequestOffset < _parts.size() * part); } int64 VideoPreload::takeNextRequestOffset() { Expects(readyToRequest()); _requestedOffsets.emplace(_nextRequestOffset); _nextRequestOffset += Storage::kDownloadPartSize; return _requestedOffsets.back(); } bool VideoPreload::feedPart( int64 offset, const QByteArray &bytes) { Expects(offset < _parts.size() * Storage::kDownloadPartSize); Expects(_requestedOffsets.contains(int(offset))); Expects(bytes.size() <= Storage::kDownloadPartSize); const auto part = Storage::kDownloadPartSize; _requestedOffsets.remove(int(offset)); _parts[offset] = bytes; if ((_nextRequestOffset + part >= _parts.size() * part) && _requestedOffsets.empty()) { _finished = true; removeFromQueue(); auto result = ::Media::Streaming::SerializeComplexPartsMap(_parts); if (result.size() == _full) { // Make sure it is parsed as a complex map. result.push_back(char(0)); } done(std::move(result)); } return true; } void VideoPreload::cancelOnFail() { _failed = true; cancelAllRequests(); done({}); } bool VideoPreload::setWebFileSizeHook(int64 size) { _failed = true; cancelAllRequests(); done({}); return false; } } // namespace Data