2019-02-13 18:10:18 +00:00
|
|
|
/*
|
|
|
|
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 "media/streaming/media_streaming_loader_mtproto.h"
|
|
|
|
|
|
|
|
#include "apiwrap.h"
|
2019-03-25 09:17:47 +00:00
|
|
|
#include "auth_session.h"
|
2019-02-25 17:26:08 +00:00
|
|
|
#include "storage/cache/storage_cache_types.h"
|
2019-02-13 18:10:18 +00:00
|
|
|
|
|
|
|
namespace Media {
|
|
|
|
namespace Streaming {
|
|
|
|
namespace {
|
|
|
|
|
2019-03-25 09:17:47 +00:00
|
|
|
constexpr auto kMaxConcurrentRequests = 4;
|
2019-02-13 18:10:18 +00:00
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
LoaderMtproto::LoaderMtproto(
|
|
|
|
not_null<ApiWrap*> api,
|
2019-03-25 09:17:47 +00:00
|
|
|
const StorageFileLocation &location,
|
2019-02-13 18:10:18 +00:00
|
|
|
int size,
|
|
|
|
Data::FileOrigin origin)
|
|
|
|
: _api(api)
|
|
|
|
, _location(location)
|
|
|
|
, _size(size)
|
|
|
|
, _origin(origin) {
|
|
|
|
}
|
|
|
|
|
2019-02-25 17:26:08 +00:00
|
|
|
std::optional<Storage::Cache::Key> LoaderMtproto::baseCacheKey() const {
|
2019-03-25 09:17:47 +00:00
|
|
|
return _location.bigFileBaseCacheKey();
|
2019-02-25 17:26:08 +00:00
|
|
|
}
|
|
|
|
|
2019-02-13 18:10:18 +00:00
|
|
|
int LoaderMtproto::size() const {
|
|
|
|
return _size;
|
|
|
|
}
|
|
|
|
|
2019-02-25 17:26:08 +00:00
|
|
|
void LoaderMtproto::load(int offset) {
|
|
|
|
crl::on_main(this, [=] {
|
|
|
|
if (_requests.contains(offset)) {
|
|
|
|
return;
|
|
|
|
} else if (_requested.add(offset)) {
|
|
|
|
sendNext();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderMtproto::stop() {
|
|
|
|
crl::on_main(this, [=] {
|
|
|
|
ranges::for_each(
|
|
|
|
base::take(_requests),
|
|
|
|
_sender.requestCanceller(),
|
|
|
|
&base::flat_map<int, mtpRequestId>::value_type::second);
|
|
|
|
_requested.clear();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderMtproto::cancel(int offset) {
|
2019-02-13 18:10:18 +00:00
|
|
|
crl::on_main(this, [=] {
|
2019-02-25 17:26:08 +00:00
|
|
|
if (const auto requestId = _requests.take(offset)) {
|
|
|
|
_sender.request(*requestId).cancel();
|
|
|
|
sendNext();
|
|
|
|
} else {
|
|
|
|
_requested.remove(offset);
|
|
|
|
}
|
2019-02-13 18:10:18 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-02-25 17:26:08 +00:00
|
|
|
void LoaderMtproto::increasePriority() {
|
|
|
|
crl::on_main(this, [=] {
|
|
|
|
_requested.increasePriority();
|
|
|
|
});
|
|
|
|
}
|
2019-02-13 18:10:18 +00:00
|
|
|
|
2019-02-25 17:26:08 +00:00
|
|
|
void LoaderMtproto::sendNext() {
|
|
|
|
if (_requests.size() >= kMaxConcurrentRequests) {
|
2019-02-13 18:10:18 +00:00
|
|
|
return;
|
2019-02-25 17:26:08 +00:00
|
|
|
}
|
|
|
|
const auto offset = _requested.take().value_or(-1);
|
|
|
|
if (offset < 0) {
|
2019-02-13 18:10:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static auto DcIndex = 0;
|
2019-03-25 09:17:47 +00:00
|
|
|
const auto usedFileReference = _location.fileReference();
|
2019-02-13 18:10:18 +00:00
|
|
|
const auto id = _sender.request(MTPupload_GetFile(
|
2019-03-25 09:17:47 +00:00
|
|
|
_location.tl(Auth().userId()),
|
2019-02-13 18:10:18 +00:00
|
|
|
MTP_int(offset),
|
|
|
|
MTP_int(kPartSize)
|
|
|
|
)).done([=](const MTPupload_File &result) {
|
|
|
|
requestDone(offset, result);
|
|
|
|
}).fail([=](const RPCError &error) {
|
2019-03-25 09:17:47 +00:00
|
|
|
requestFailed(offset, error, usedFileReference);
|
|
|
|
}).toDC(MTP::downloadDcId(
|
|
|
|
_location.dcId(),
|
|
|
|
(++DcIndex) % MTP::kDownloadSessionsCount
|
|
|
|
)).send();
|
2019-02-13 18:10:18 +00:00
|
|
|
_requests.emplace(offset, id);
|
|
|
|
|
2019-02-25 17:26:08 +00:00
|
|
|
sendNext();
|
2019-02-13 18:10:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderMtproto::requestDone(int offset, const MTPupload_File &result) {
|
|
|
|
result.match([&](const MTPDupload_file &data) {
|
|
|
|
_requests.erase(offset);
|
2019-02-25 17:26:08 +00:00
|
|
|
sendNext();
|
2019-02-13 18:10:18 +00:00
|
|
|
_parts.fire({ offset, data.vbytes.v });
|
|
|
|
}, [&](const MTPDupload_fileCdnRedirect &data) {
|
|
|
|
changeCdnParams(
|
|
|
|
offset,
|
|
|
|
data.vdc_id.v,
|
|
|
|
data.vfile_token.v,
|
|
|
|
data.vencryption_key.v,
|
|
|
|
data.vencryption_iv.v,
|
|
|
|
data.vfile_hashes.v);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderMtproto::changeCdnParams(
|
|
|
|
int offset,
|
|
|
|
MTP::DcId dcId,
|
|
|
|
const QByteArray &token,
|
|
|
|
const QByteArray &encryptionKey,
|
|
|
|
const QByteArray &encryptionIV,
|
|
|
|
const QVector<MTPFileHash> &hashes) {
|
2019-03-01 12:22:47 +00:00
|
|
|
// #TODO streaming later cdn
|
|
|
|
_parts.fire({ LoadedPart::kFailedOffset });
|
2019-02-13 18:10:18 +00:00
|
|
|
}
|
|
|
|
|
2019-03-01 12:22:47 +00:00
|
|
|
void LoaderMtproto::requestFailed(
|
|
|
|
int offset,
|
|
|
|
const RPCError &error,
|
|
|
|
const QByteArray &usedFileReference) {
|
2019-02-13 18:10:18 +00:00
|
|
|
const auto &type = error.type();
|
2019-03-01 12:22:47 +00:00
|
|
|
const auto fail = [=] {
|
2019-02-13 18:10:18 +00:00
|
|
|
_parts.fire({ LoadedPart::kFailedOffset });
|
2019-03-01 12:22:47 +00:00
|
|
|
};
|
|
|
|
if (error.code() != 400 || !type.startsWith(qstr("FILE_REFERENCE_"))) {
|
|
|
|
return fail();
|
2019-02-13 18:10:18 +00:00
|
|
|
}
|
|
|
|
const auto callback = [=](const Data::UpdatedFileReferences &updated) {
|
2019-03-25 09:17:47 +00:00
|
|
|
_location.refreshFileReference(updated);
|
|
|
|
if (_location.fileReference() == usedFileReference) {
|
|
|
|
fail();
|
|
|
|
} else if (!_requests.take(offset)) {
|
|
|
|
// Request with such offset was already cancelled.
|
|
|
|
return;
|
|
|
|
} else {
|
2019-03-01 12:22:47 +00:00
|
|
|
_requested.add(offset);
|
|
|
|
sendNext();
|
2019-03-25 09:17:47 +00:00
|
|
|
}
|
2019-02-13 18:10:18 +00:00
|
|
|
};
|
|
|
|
_api->refreshFileReference(_origin, crl::guard(this, callback));
|
|
|
|
}
|
|
|
|
|
|
|
|
rpl::producer<LoadedPart> LoaderMtproto::parts() const {
|
|
|
|
return _parts.events();
|
|
|
|
}
|
|
|
|
|
|
|
|
LoaderMtproto::~LoaderMtproto() = default;
|
|
|
|
|
|
|
|
} // namespace Streaming
|
|
|
|
} // namespace Media
|