Start using document bytes from DocumentMedia.

This commit is contained in:
John Preston 2020-04-09 16:27:53 +04:00
parent 888e42df34
commit 33f4946242
31 changed files with 190 additions and 169 deletions

View file

@ -11,23 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rect_part.h"
enum class ImageRoundRadius;
class MainWindow;
class MainWidget;
class HistoryItem;
class History;
namespace HistoryView {
class Element;
} // namespace HistoryView
namespace Media {
namespace Clip {
class Reader;
} // namespace Clip
} // namespace Media
using HistoryItemsMap = base::flat_set<not_null<HistoryItem*>>;
using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
enum RoundCorners : int {
SmallMaskCorners = 0x00, // for images
LargeMaskCorners,

View file

@ -315,9 +315,10 @@ void EditCaptionBox::prepareGifPreview(DocumentData* document) {
const auto callback = [=](Media::Clip::Notification notification) {
clipCallback(notification);
};
_gifMedia = document ? document->createMediaView() : nullptr;
if (document && document->isAnimation() && document->loaded()) {
_gifPreview = Media::Clip::MakeReader(
document,
_gifMedia.get(),
_msgId,
callback);
} else if (!isListEmpty) {

View file

@ -23,6 +23,7 @@ class SessionController;
namespace Data {
class Media;
class DocumentMedia;
} // namespace Data
namespace Ui {
@ -94,6 +95,7 @@ private:
bool _doc = false;
QPixmap _thumb;
std::shared_ptr<Data::DocumentMedia> _gifMedia;
Media::Clip::ReaderPointer _gifPreview;
object_ptr<Ui::InputField> _field = { nullptr };

View file

@ -604,7 +604,7 @@ void StickerSetBox::Inner::setupLottie(int index) {
element.animated = Stickers::LottieAnimationFromDocument(
getLottiePlayer(),
document,
element.documentMedia.get(),
Stickers::LottieSize::StickerSet,
boundingBoxSize() * cIntRetinaFactor());
}

View file

@ -642,6 +642,7 @@ StickersBox::Inner::Row::Row(
, accessHash(accessHash)
, thumbnail(thumbnail)
, sticker(sticker)
, stickerMedia(sticker ? sticker->createMediaView() : nullptr)
, count(count)
, title(title)
, titleWidth(titleWidth)
@ -947,7 +948,7 @@ void StickersBox::Inner::validateLottieAnimation(not_null<Row*> set) {
}
auto player = Stickers::LottieThumbnail(
set->thumbnail,
set->sticker,
set->stickerMedia.get(),
Stickers::LottieSize::SetsListThumbnail,
QSize(
st::contactsPhotoSize,
@ -1645,6 +1646,7 @@ void StickersBox::Inner::updateRows() {
}
row->thumbnail = thumbnail;
row->sticker = sticker;
row->stickerMedia = sticker->createMediaView();
row->pixw = pixw;
row->pixh = pixh;
}

View file

@ -233,15 +233,17 @@ private:
bool removed,
int32 pixw,
int32 pixh);
~Row();
bool isRecentSet() const {
return (id == Stickers::CloudRecentSetId);
}
~Row();
uint64 id = 0;
uint64 accessHash = 0;
ImagePtr thumbnail;
DocumentData *sticker = nullptr;
std::shared_ptr<Data::DocumentMedia> stickerMedia;
int32 count = 0;
QString title;
int titleWidth = 0;

View file

@ -1008,7 +1008,7 @@ auto FieldAutocompleteInner::getLottieRenderer()
void FieldAutocompleteInner::setupLottie(StickerSuggestion &suggestion) {
const auto document = suggestion.document;
suggestion.animated = Stickers::LottiePlayerFromDocument(
document,
suggestion.documentMedia.get(),
Stickers::LottieSize::InlineResults,
stickerBoundingBox() * cIntRetinaFactor(),
Lottie::Quality::Default,

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "stickers.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "boxes/stickers_box.h"
@ -1146,10 +1147,11 @@ auto LottieCachedFromContent(
template <typename Method>
auto LottieFromDocument(
Method &&method,
not_null<DocumentData*> document,
not_null<Data::DocumentMedia*> media,
uint8 keyShift,
QSize box) {
const auto data = document->data();
const auto document = media->owner();
const auto data = media->bytes();
const auto filepath = document->filepath();
if (box.width() * box.height() > kDontCacheLottieAfterArea) {
// Don't use frame caching for large stickers.
@ -1172,13 +1174,13 @@ auto LottieFromDocument(
}
std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
not_null<DocumentData*> document,
not_null<Data::DocumentMedia*> media,
LottieSize sizeTag,
QSize box,
Lottie::Quality quality,
std::shared_ptr<Lottie::FrameRenderer> renderer) {
return LottiePlayerFromDocument(
document,
media,
nullptr,
sizeTag,
box,
@ -1187,7 +1189,7 @@ std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
}
std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
not_null<DocumentData*> document,
not_null<Data::DocumentMedia*> media,
const Lottie::ColorReplacements *replacements,
LottieSize sizeTag,
QSize box,
@ -1202,18 +1204,18 @@ std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
};
const auto tag = replacements ? replacements->tag : uint8(0);
const auto keyShift = ((tag << 4) & 0xF0) | (uint8(sizeTag) & 0x0F);
return LottieFromDocument(method, document, uint8(keyShift), box);
return LottieFromDocument(method, media, uint8(keyShift), box);
}
not_null<Lottie::Animation*> LottieAnimationFromDocument(
not_null<Lottie::MultiPlayer*> player,
not_null<DocumentData*> document,
not_null<Data::DocumentMedia*> media,
LottieSize sizeTag,
QSize box) {
const auto method = [&](auto &&...args) {
return player->append(std::forward<decltype(args)>(args)...);
};
return LottieFromDocument(method, document, uint8(sizeTag), box);
return LottieFromDocument(method, media, uint8(sizeTag), box);
}
bool HasLottieThumbnail(
@ -1243,19 +1245,20 @@ bool HasLottieThumbnail(
std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
ImagePtr thumbnail,
not_null<DocumentData*> sticker,
not_null<Data::DocumentMedia*> media,
LottieSize sizeTag,
QSize box,
std::shared_ptr<Lottie::FrameRenderer> renderer) {
const auto document = media->owner();
const auto baseKey = thumbnail
? thumbnail->location().file().bigFileBaseCacheKey()
: sticker->bigFileBaseCacheKey();
: document->bigFileBaseCacheKey();
if (!baseKey) {
return nullptr;
}
const auto content = (thumbnail
? thumbnail->bytesForCache()
: Lottie::ReadContent(sticker->data(), sticker->filepath()));
: Lottie::ReadContent(media->bytes(), document->filepath()));
if (content.isEmpty()) {
return nullptr;
}
@ -1267,7 +1270,7 @@ std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
method,
*baseKey,
uint8(sizeTag),
&sticker->session(),
&document->session(),
content,
box);
}

View file

@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class DocumentData;
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Main {
class Session;
} // namespace Main
@ -136,13 +140,13 @@ enum class LottieSize : uchar {
};
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
not_null<DocumentData*> document,
not_null<Data::DocumentMedia*> media,
LottieSize sizeTag,
QSize box,
Lottie::Quality quality = Lottie::Quality(),
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
not_null<DocumentData*> document,
not_null<Data::DocumentMedia*> media,
const Lottie::ColorReplacements *replacements,
LottieSize sizeTag,
QSize box,
@ -150,7 +154,7 @@ enum class LottieSize : uchar {
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);
[[nodiscard]] not_null<Lottie::Animation*> LottieAnimationFromDocument(
not_null<Lottie::MultiPlayer*> player,
not_null<DocumentData*> document,
not_null<Data::DocumentMedia*> media,
LottieSize sizeTag,
QSize box);
@ -159,7 +163,7 @@ enum class LottieSize : uchar {
not_null<DocumentData*> sticker);
[[nodiscard]] std::unique_ptr<Lottie::SinglePlayer> LottieThumbnail(
ImagePtr thumbnail,
not_null<DocumentData*> sticker,
not_null<Data::DocumentMedia*> media,
LottieSize sizeTag,
QSize box,
std::shared_ptr<Lottie::FrameRenderer> renderer = nullptr);

View file

@ -67,6 +67,7 @@ struct StickerIcon {
: setId(setId)
, thumbnail(thumbnail)
, sticker(sticker)
, stickerMedia(sticker ? sticker->createMediaView() : nullptr)
, pixw(pixw)
, pixh(pixh) {
}
@ -74,6 +75,7 @@ struct StickerIcon {
ImagePtr thumbnail;
mutable Lottie::SinglePlayer *lottie = nullptr;
DocumentData *sticker = nullptr;
std::shared_ptr<Data::DocumentMedia> stickerMedia;
ChannelData *megagroup = nullptr;
int pixw = 0;
int pixh = 0;
@ -704,7 +706,7 @@ void StickersListWidget::Footer::validateIconLottieAnimation(
}
auto player = Stickers::LottieThumbnail(
icon.thumbnail,
icon.sticker,
icon.stickerMedia.get(),
Stickers::LottieSize::StickersFooter,
QSize(
st::stickerIconWidth - 2 * st::stickerIconPadding,
@ -1692,7 +1694,7 @@ void StickersListWidget::setupLottie(Set &set, int section, int index) {
ensureLottiePlayer(set);
sticker.animated = Stickers::LottieAnimationFromDocument(
set.lottiePlayer,
document,
sticker.documentMedia.get(),
Stickers::LottieSize::StickersPanel,
boundingBoxSize() * cIntRetinaFactor());
_lottieData[set.id].items.emplace(

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "data/data_document_media.h"
#include "main/main_session.h"
#include "boxes/confirm_box.h"
#include "core/application.h" // Core::App().showTheme.
@ -178,9 +179,11 @@ void CloudThemes::showPreview(const CloudTheme &cloud) {
void CloudThemes::applyFromDocument(const CloudTheme &cloud) {
const auto document = _session->data().document(cloud.documentId);
loadDocumentAndInvoke(_updatingFrom, cloud, document, [=] {
loadDocumentAndInvoke(_updatingFrom, cloud, document, [=](
std::shared_ptr<Data::DocumentMedia> media) {
const auto document = media->owner();
auto preview = Window::Theme::PreviewFromFile(
document->data(),
media->bytes(),
document->location().name(),
cloud);
if (preview) {
@ -192,7 +195,9 @@ void CloudThemes::applyFromDocument(const CloudTheme &cloud) {
void CloudThemes::previewFromDocument(const CloudTheme &cloud) {
const auto document = _session->data().document(cloud.documentId);
loadDocumentAndInvoke(_previewFrom, cloud, document, [=] {
loadDocumentAndInvoke(_previewFrom, cloud, document, [=](
std::shared_ptr<Data::DocumentMedia> media) {
const auto document = media->owner();
Core::App().showTheme(document, cloud);
});
}
@ -201,12 +206,13 @@ void CloudThemes::loadDocumentAndInvoke(
LoadingDocument &value,
const CloudTheme &cloud,
not_null<DocumentData*> document,
Fn<void()> callback) {
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback) {
const auto alreadyWaiting = (value.document != nullptr);
if (alreadyWaiting) {
value.document->cancel();
}
value.document = document;
value.documentMedia = document->createMediaView();
value.document->save(
Data::FileOriginTheme(cloud.id, cloud.accessHash),
QString());
@ -228,8 +234,9 @@ void CloudThemes::loadDocumentAndInvoke(
void CloudThemes::invokeForLoaded(LoadingDocument &value) {
const auto onstack = std::move(value.callback);
auto media = std::move(value.documentMedia);
value = LoadingDocument();
onstack();
onstack(std::move(media));
}
void CloudThemes::scheduleReload() {

View file

@ -17,6 +17,8 @@ class Session;
namespace Data {
class DocumentMedia;
struct CloudTheme {
uint64 id = 0;
uint64 accessHash = 0;
@ -54,8 +56,9 @@ private:
struct LoadingDocument {
CloudTheme theme;
DocumentData *document = nullptr;
std::shared_ptr<Data::DocumentMedia> documentMedia;
rpl::lifetime subscription;
Fn<void()> callback;
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback;
};
void parseThemes(const QVector<MTPTheme> &list);
@ -71,7 +74,7 @@ private:
LoadingDocument &value,
const CloudTheme &cloud,
not_null<DocumentData*> document,
Fn<void()> callback);
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback);
void invokeForLoaded(LoadingDocument &value);
const not_null<Main::Session*> _session;

View file

@ -587,7 +587,10 @@ void DocumentData::validateLottieSticker() {
}
void DocumentData::setDataAndCache(const QByteArray &data) {
setData(data);
_data = data;
if (const auto media = activeMediaView()) {
media->setBytes(data);
}
if (saveToCache() && data.size() <= Storage::kMaxFileInMemory) {
owner().cache().put(
cacheKey(),
@ -734,11 +737,12 @@ auto DocumentData::bigFileBaseCacheKey() const
}
bool DocumentData::saveToCache() const {
return (type == StickerDocument && size < Storage::kMaxStickerInMemory)
|| (isAnimation() && size < Storage::kMaxAnimationInMemory)
|| (isVoiceMessage() && size < Storage::kMaxVoiceInMemory)
|| (type == WallPaperDocument)
|| (isTheme() && size < Storage::kMaxFileInMemory);
return (size < Storage::kMaxFileInMemory)
&& ((type == StickerDocument)
|| isAnimation()
|| isVoiceMessage()
|| (type == WallPaperDocument)
|| isTheme());
}
void DocumentData::unload() {
@ -814,6 +818,7 @@ bool DocumentData::loaded(FilePathResolve resolve) const {
that->setGoodThumbnailDataReady();
if (const auto media = activeMediaView()) {
media->setBytes(_loader->bytes());
media->checkStickerLarge(_loader.get());
}
destroyLoader();
@ -824,7 +829,7 @@ bool DocumentData::loaded(FilePathResolve resolve) const {
}
_owner->notifyDocumentLayoutChanged(this);
}
return !data().isEmpty() || !filepath(resolve).isEmpty();
return !rawBytes().isEmpty() || !filepath(resolve).isEmpty();
}
void DocumentData::destroyLoader() const {
@ -918,10 +923,10 @@ void DocumentData::save(
if (loaded(FilePathResolve::Checked)) {
auto &l = location(true);
if (!toFile.isEmpty()) {
if (!_data.isEmpty()) {
if (!rawBytes().isEmpty()) {
QFile f(toFile);
f.open(QIODevice::WriteOnly);
f.write(_data);
f.write(rawBytes());
f.close();
setLocation(FileLocation(toFile));
@ -1085,7 +1090,7 @@ QByteArray documentWaveformEncode5bit(const VoiceWaveform &waveform) {
return result;
}
QByteArray DocumentData::data() const {
QByteArray DocumentData::rawBytes() const {
if (!_data.isEmpty()) {
ActiveCache().up(const_cast<DocumentData*>(this));
}
@ -1115,8 +1120,10 @@ void DocumentData::setLocation(const FileLocation &loc) {
QString DocumentData::filepath(FilePathResolve resolve) const {
bool check = (resolve != FilePathResolve::Cached);
QString result = (check && _location.name().isEmpty()) ? QString() : location(check).name();
bool saveFromData = result.isEmpty() && !data().isEmpty();
QString result = (check && _location.name().isEmpty())
? QString()
: location(check).name();
bool saveFromData = result.isEmpty() && !rawBytes().isEmpty();
if (saveFromData) {
if (resolve != FilePathResolve::SaveFromData
&& resolve != FilePathResolve::SaveFromDataSilent) {
@ -1131,7 +1138,7 @@ QString DocumentData::filepath(FilePathResolve resolve) const {
if (!filename.isEmpty()) {
QFile f(filename);
if (f.open(QIODevice::WriteOnly)) {
if (f.write(data()) == data().size()) {
if (f.write(rawBytes()) == rawBytes().size()) {
f.close();
const_cast<DocumentData*>(this)->_location = FileLocation(filename);
Local::writeFileLocation(mediaKey(), _location);
@ -1263,8 +1270,8 @@ auto DocumentData::createStreamingLoader(
}
if (!forceRemoteLoader) {
const auto &location = this->location(true);
if (!data().isEmpty()) {
return Media::Streaming::MakeBytesLoader(data());
if (!rawBytes().isEmpty()) {
return Media::Streaming::MakeBytesLoader(rawBytes());
} else if (!location.isEmpty() && location.accessEnable()) {
auto result = Media::Streaming::MakeFileLoader(location.name());
location.accessDisable();
@ -1545,6 +1552,9 @@ void DocumentData::collectLocalData(not_null<DocumentData*> local) {
if (!local->_data.isEmpty()) {
ActiveCache().decrement(_data.size());
_data = local->_data;
if (const auto media = activeMediaView()) {
media->setBytes(local->_data);
}
ActiveCache().increment(_data.size());
if (!_data.isEmpty()) {
ActiveCache().up(this);
@ -1642,7 +1652,7 @@ base::binary_guard ReadImageAsync(
FnMut<void(QImage&&)> done) {
auto result = base::binary_guard();
crl::async([
bytes = document->data(),
bytes = document->rawBytes(),
path = document->filepath(),
postprocess = std::move(postprocess),
guard = result.make_guard(),
@ -1672,30 +1682,4 @@ base::binary_guard ReadImageAsync(
return result;
}
//void HandleUnsupportedMedia(
// not_null<DocumentData*> document,
// FullMsgId contextId) {
// using Error = ::Media::Streaming::Error;
//
// document->setInappPlaybackFailed();
// const auto filepath = document->filepath(
// DocumentData::FilePathResolve::SaveFromData);
// if (filepath.isEmpty()) {
// const auto save = [=] {
// Ui::hideLayer();
// DocumentSaveClickHandler::Save(
// (contextId ? contextId : Data::FileOrigin()),
// document,
// document->owner().message(contextId));
// };
// Ui::show(Box<ConfirmBox>(
// tr::lng_player_cant_stream(tr::now),
// tr::lng_player_download(tr::now),
// tr::lng_cancel(tr::now),
// save));
// } else if (IsValidMediaFile(filepath)) {
// File::Launch(filepath);
// }
//}
} // namespace Data

View file

@ -127,7 +127,7 @@ public:
void setWaitingForAlbum();
[[nodiscard]] bool waitingForAlbum() const;
[[nodiscard]] QByteArray data() const;
[[nodiscard]] QByteArray rawBytes() const;
[[nodiscard]] const FileLocation &location(bool check = false) const;
void setLocation(const FileLocation &loc);
@ -163,9 +163,6 @@ public:
void recountIsImage();
[[nodiscard]] bool supportsStreaming() const;
void setNotSupportsStreaming();
void setData(const QByteArray &data) {
_data = data;
}
void setDataAndCache(const QByteArray &data);
bool checkWallPaperProperties();
[[nodiscard]] bool isWallPaper() const;

View file

@ -82,11 +82,16 @@ enum class FileType {
} // namespace
DocumentMedia::DocumentMedia(not_null<DocumentData*> owner)
: _owner(owner) {
: _owner(owner)
, _bytes(owner->rawBytes()) {
}
DocumentMedia::~DocumentMedia() = default;
not_null<DocumentData*> DocumentMedia::owner() const {
return _owner;
}
void DocumentMedia::goodThumbnailWanted() {
_flags |= Flag::GoodThumbnailWanted;
}
@ -135,7 +140,7 @@ void DocumentMedia::checkStickerLarge() {
if (data->animated || !_owner->loaded()) {
return;
}
const auto bytes = _owner->data();
const auto bytes = _owner->rawBytes();
if (bytes.isEmpty()) {
const auto &loc = _owner->location(true);
if (loc.accessEnable()) {
@ -155,6 +160,16 @@ void DocumentMedia::checkStickerLarge() {
}
}
void DocumentMedia::setBytes(const QByteArray &bytes) {
if (!bytes.isEmpty()) {
_bytes = bytes;
}
}
QByteArray DocumentMedia::bytes() const {
return _bytes;
}
void DocumentMedia::checkStickerSmall() {
const auto data = _owner->sticker();
if ((data && data->animated) || _owner->thumbnailEnoughForSticker()) {
@ -184,18 +199,18 @@ void DocumentMedia::checkStickerLarge(not_null<FileLoader*> loader) {
if (_owner->sticker()
&& !_sticker
&& !loader->imageData().isNull()
&& !_owner->data().isEmpty()) {
&& !_owner->rawBytes().isEmpty()) {
_sticker = std::make_unique<Image>(
std::make_unique<Images::LocalFileSource>(
QString(),
_owner->data(),
_owner->rawBytes(),
loader->imageFormat(),
loader->imageData()));
}
}
void DocumentMedia::GenerateGoodThumbnail(not_null<DocumentData*> document) {
const auto data = document->data();
const auto data = document->rawBytes();
const auto type = document->isWallPaper()
? FileType::WallPaper
: document->isTheme()

View file

@ -18,6 +18,8 @@ public:
explicit DocumentMedia(not_null<DocumentData*> owner);
~DocumentMedia();
[[nodiscard]] not_null<DocumentData*> owner() const;
void goodThumbnailWanted();
[[nodiscard]] Image *goodThumbnail() const;
void setGoodThumbnail(QImage thumbnail);
@ -30,6 +32,9 @@ public:
[[nodiscard]] Image *getStickerLarge();
void checkStickerLarge(not_null<FileLoader*> loader);
void setBytes(const QByteArray &bytes);
[[nodiscard]] QByteArray bytes() const;
// For DocumentData.
static void CheckGoodThumbnail(not_null<DocumentData*> document);
@ -47,6 +52,7 @@ private:
std::unique_ptr<Image> _goodThumbnail;
mutable std::unique_ptr<Image> _inlineThumbnail;
std::unique_ptr<Image> _sticker;
QByteArray _bytes;
Flags _flags;
};

View file

@ -386,6 +386,8 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti
auto voiceStatusOverride = QString();
if (const auto voice = Get<HistoryDocumentVoice>()) {
ensureDataMediaCreated();
const VoiceWaveform *wf = nullptr;
uchar norm_value = 0;
if (const auto voiceData = _data->voice()) {
@ -393,7 +395,7 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti
if (wf->isEmpty()) {
wf = nullptr;
if (loaded) {
Local::countVoiceWaveform(_data);
Local::countVoiceWaveform(_dataMedia.get());
}
} else if (wf->at(0) < 0) {
wf = nullptr;

View file

@ -271,8 +271,10 @@ void Sticker::setDiceIndex(const QString &emoji, int index) {
}
void Sticker::setupLottie() {
Expects(_dataMedia != nullptr);
_lottie = Stickers::LottiePlayerFromDocument(
_data,
_dataMedia.get(),
_replacements,
Stickers::LottieSize::MessageHistory,
_size * cIntRetinaFactor(),

View file

@ -146,6 +146,9 @@ int Gif::resizeGetHeight(int width) {
void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
const auto document = getShownDocument();
if (document) {
ensureDataMediaCreated(document);
}
document->automaticLoad(fileOrigin(), nullptr);
bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
@ -154,7 +157,7 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
&& !_gif.isBad()
&& CanPlayInline(document)) {
auto that = const_cast<Gif*>(this);
that->_gif = Media::Clip::MakeReader(document, FullMsgId(), [that](Media::Clip::Notification notification) {
that->_gif = Media::Clip::MakeReader(_dataMedia.get(), FullMsgId(), [that](Media::Clip::Notification notification) {
that->clipCallback(notification);
});
if (_gif) _gif->setAutoplay();
@ -508,9 +511,11 @@ QSize Sticker::getThumbSize() const {
return QSize(qMax(w, 1), qMax(h, 1));
}
void Sticker::setupLottie(not_null<DocumentData*> document) const {
void Sticker::setupLottie() const {
Expects(_dataMedia != nullptr);
_lottie = Stickers::LottiePlayerFromDocument(
document,
_dataMedia.get(),
Stickers::LottieSize::InlineResults,
QSize(
st::stickerPanSize.width() - st::buttonRadius * 2,
@ -530,7 +535,7 @@ void Sticker::prepareThumbnail() const {
&& document->sticker()
&& document->sticker()->animated
&& document->loaded()) {
setupLottie(document);
setupLottie();
}
_dataMedia->checkStickerSmall();
if (const auto sticker = _dataMedia->getStickerSmall()) {
@ -1318,7 +1323,10 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
// Gif thumb
auto thumbDisplayed = false, radial = false;
auto document = getResultDocument();
const auto document = getResultDocument();
if (document) {
ensureDataMediaCreated(document);
}
auto animatedThumb = document && document->isAnimation();
if (animatedThumb) {
document->automaticLoad(fileOrigin(), nullptr);
@ -1326,7 +1334,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
if (loaded && !_gif && !_gif.isBad()) {
auto that = const_cast<Game*>(this);
that->_gif = Media::Clip::MakeReader(document, FullMsgId(), [that](Media::Clip::Notification notification) {
that->_gif = Media::Clip::MakeReader(_dataMedia.get(), FullMsgId(), [that](Media::Clip::Notification notification) {
that->clipCallback(notification);
});
if (_gif) _gif->setAutoplay();
@ -1403,7 +1411,7 @@ void Game::prepareThumbnail(QSize size) const {
validateThumbnail(photo->thumbnail(), size, true);
validateThumbnail(photo->thumbnailInline(), size, false);
} else if (const auto document = getResultDocument()) {
ensureDataMediaCreated(document);
Assert(_dataMedia != nullptr);
validateThumbnail(document->thumbnail(), size, true);
validateThumbnail(_dataMedia->thumbnailInline(), size, false);
}

View file

@ -193,7 +193,7 @@ public:
private:
void ensureDataMediaCreated(not_null<DocumentData*> document) const;
void setupLottie(not_null<DocumentData*> document) const;
void setupLottie() const;
QSize getThumbSize() const;
void prepareThumbnail() const;

View file

@ -761,7 +761,6 @@ void Mixer::play(
auto type = audio.type();
AudioMsgId stopped;
auto notLoadedYet = false;
{
QMutexLocker lock(&AudioMutex);
Audio::AttachToDevice();
@ -799,42 +798,18 @@ void Mixer::play(
current->state.id = audio;
current->lastUpdateWhen = 0;
current->lastUpdatePosition = 0;
if (externalData) {
current->setExternalData(std::move(externalData));
} else {
current->setExternalData(nullptr);
current->file = audio.audio()->location(true);
current->data = audio.audio()->data();
notLoadedYet = (current->file.isEmpty() && current->data.isEmpty());
}
if (notLoadedYet) {
auto newState = (type == AudioMsgId::Type::Song)
? State::Stopped
: State::StoppedAtError;
setStoppedState(current, newState);
} else {
current->state.position = (positionMs * current->state.frequency)
/ 1000LL;
current->state.state = current->externalData
? State::Paused
: fadedStart
? State::Starting
: State::Playing;
current->loading = true;
emit loaderOnStart(current->state.id, positionMs);
if (type == AudioMsgId::Type::Voice) {
emit suppressSong();
}
}
}
if (notLoadedYet) {
if (type == AudioMsgId::Type::Song || type == AudioMsgId::Type::Video) {
DocumentOpenClickHandler::Open(
audio.contextId(),
audio.audio(),
Auth().data().message(audio.contextId()));
} else {
onError(audio);
current->setExternalData(std::move(externalData));
current->state.position = (positionMs * current->state.frequency)
/ 1000LL;
current->state.state = current->externalData
? State::Paused
: fadedStart
? State::Starting
: State::Playing;
current->loading = true;
emit loaderOnStart(current->state.id, positionMs);
if (type == AudioMsgId::Type::Voice) {
emit suppressSong();
}
}
if (stopped) {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/clip/media_clip_reader.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "storage/file_download.h"
#include "media/clip/media_clip_ffmpeg.h"
#include "media/clip/media_clip_check_streaming.h"
@ -91,15 +92,20 @@ Reader::Reader(const QString &filepath, Callback &&callback, Mode mode, crl::tim
init(FileLocation(filepath), QByteArray());
}
Reader::Reader(not_null<DocumentData*> document, FullMsgId msgId, Callback &&callback, Mode mode, crl::time seekMs)
Reader::Reader(
not_null<Data::DocumentMedia*> media,
FullMsgId msgId,
Callback &&callback,
Mode mode,
crl::time seekMs)
: _callback(std::move(callback))
, _mode(mode)
, _audioMsgId(
document,
media->owner(),
msgId,
(mode == Mode::Video) ? AudioMsgId::CreateExternalPlayId() : 0)
, _seekPositionMs(seekMs) {
init(document->location(), document->data());
init(media->owner()->location(), media->bytes());
}
Reader::Reader(const QByteArray &data, Callback &&callback, Mode mode, crl::time seekMs)

View file

@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class FileLocation;
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Media {
namespace Clip {
@ -56,8 +60,8 @@ public:
Video,
};
Reader(not_null<Data::DocumentMedia*> media, FullMsgId msgId, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0);
Reader(const QString &filepath, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0);
Reader(not_null<DocumentData*> document, FullMsgId msgId, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0);
Reader(const QByteArray &data, Callback &&callback, Mode mode = Mode::Gif, crl::time seekMs = 0);
// Reader can be already deleted.

View file

@ -1220,8 +1220,9 @@ void OverlayWidget::notifyFileDialogShown(bool shown) {
void OverlayWidget::onSaveAs() {
QString file;
if (_doc) {
const FileLocation &location(_doc->location(true));
if (!_doc->data().isEmpty() || location.accessEnable()) {
const auto &location = _doc->location(true);
const auto bytes = _docMedia->bytes();
if (!bytes.isEmpty() || location.accessEnable()) {
QFileInfo alreadyInfo(location.name());
QDir alreadyDir(alreadyInfo.dir());
QString name = alreadyInfo.fileName(), filter;
@ -1240,17 +1241,19 @@ void OverlayWidget::onSaveAs() {
file = FileNameForSave(tr::lng_save_file(tr::now), filter, qsl("doc"), name, true, alreadyDir);
if (!file.isEmpty() && file != location.name()) {
if (_doc->data().isEmpty()) {
if (bytes.isEmpty()) {
QFile(file).remove();
QFile(location.name()).copy(file);
} else {
QFile f(file);
f.open(QIODevice::WriteOnly);
f.write(_doc->data());
f.write(bytes);
}
}
if (_doc->data().isEmpty()) location.accessDisable();
if (bytes.isEmpty()) {
location.accessDisable();
}
} else {
DocumentSaveClickHandler::Save(
fileOrigin(),
@ -2330,7 +2333,7 @@ void OverlayWidget::initThemePreview() {
Assert(_doc && _doc->isTheme());
const auto bytes = _doc->data();
const auto bytes = _docMedia->bytes();
auto &location = _doc->location();
if (bytes.isEmpty()
&& (location.isEmpty() || !location.accessEnable())) {

View file

@ -30,11 +30,8 @@ struct Key;
// This value is used in local cache database settings!
constexpr auto kMaxFileInMemory = 10 * 1024 * 1024;
// 2 MB audio is hold in memory and auto loaded
constexpr auto kMaxVoiceInMemory = 2 * 1024 * 1024;
// 2 MB stickers hold in memory, auto loaded and displayed inline
constexpr auto kMaxStickerInMemory = 2 * 1024 * 1024;
constexpr auto kMaxStickerBytesSize = 2 * 1024 * 1024;
// 10 MB GIF and mp4 animations held in memory while playing
constexpr auto kMaxWallPaperInMemory = kMaxFileInMemory;

View file

@ -836,7 +836,7 @@ void FileLoadTask::process() {
&& (h > 0)
&& (w <= StickerMaxSize)
&& (h <= StickerMaxSize)
&& (filesize < Storage::kMaxStickerInMemory);
&& (filesize < Storage::kMaxStickerBytesSize);
if (isSticker) {
attributes.push_back(MTP_documentAttributeSticker(
MTP_flags(0),

View file

@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/stickers.h"
#include "data/data_drafts.h"
#include "data/data_user.h"
#include "data/data_session.h"
#include "data/data_document_media.h"
#include "boxes/send_files_box.h"
#include "base/flags.h"
#include "base/platform/base_platform_file_utilities.h"
@ -37,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "window/themes/window_theme.h"
#include "window/window_session_controller.h"
#include "data/data_session.h"
#include "history/history.h"
#include "facades.h"
@ -3358,11 +3359,11 @@ Storage::Cache::Database::Settings cacheBigFileSettings() {
class CountWaveformTask : public Task {
public:
CountWaveformTask(DocumentData *doc)
: _doc(doc)
, _loc(doc->location(true))
, _data(doc->data())
, _wavemax(0) {
CountWaveformTask(not_null<Data::DocumentMedia*> media)
: _doc(media->owner())
, _loc(_doc->location(true))
, _data(media->bytes())
, _wavemax(0) {
if (_data.isEmpty() && !_loc.accessEnable()) {
_doc = nullptr;
}
@ -3399,7 +3400,7 @@ public:
}
protected:
DocumentData *_doc;
DocumentData *_doc = nullptr;
FileLocation _loc;
QByteArray _data;
VoiceWaveform _waveform;
@ -3407,13 +3408,14 @@ protected:
};
void countVoiceWaveform(DocumentData *document) {
void countVoiceWaveform(not_null<Data::DocumentMedia*> media) {
const auto document = media->owner();
if (const auto voice = document->voice()) {
if (_localLoader) {
voice->waveform.resize(1 + sizeof(TaskId));
voice->waveform[0] = -1; // counting
TaskId taskId = _localLoader->addTask(
std::make_unique<CountWaveformTask>(document));
std::make_unique<CountWaveformTask>(media));
memcpy(voice->waveform.data() + 1, &taskId, sizeof(taskId));
}
}

View file

@ -18,6 +18,7 @@ class History;
namespace Data {
class WallPaper;
class DocumentMedia;
} // namespace Data
namespace Lang {
@ -129,7 +130,7 @@ Storage::EncryptionKey cacheBigFileKey();
QString cacheBigFilePath();
Storage::Cache::Database::Settings cacheBigFileSettings();
void countVoiceWaveform(DocumentData *document);
void countVoiceWaveform(not_null<Data::DocumentMedia*> media);
void cancelTask(TaskId id);

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_cloud_themes.h"
#include "data/data_file_origin.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_session.h"
#include "ui/image/image_prepare.h"
#include "ui/widgets/popup_menu.h"
@ -560,6 +561,7 @@ void CloudList::refreshColors(Element &element) {
&& (!document || !document->isTheme()))) {
element.check->setColors(ColorsFromCurrentTheme());
} else if (document) {
element.media = document ? document->createMediaView() : nullptr;
document->save(
Data::FileOriginTheme(theme.id, theme.accessHash),
QString());
@ -639,7 +641,9 @@ void CloudList::refreshColorsFromDocument(
not_null<DocumentData*> document) {
const auto id = element.id();
const auto path = document->filepath();
const auto data = document->data();
const auto data = element.media
? base::take(element.media)->bytes()
: QByteArray();
crl::async([=, guard = element.generating.make_guard()]() mutable {
crl::on_main(std::move(guard), [
=,

View file

@ -88,6 +88,7 @@ private:
Data::CloudTheme theme;
not_null<CloudListCheck*> check;
std::unique_ptr<Ui::Radiobutton> button;
std::shared_ptr<Data::DocumentMedia> media;
base::binary_guard generating;
bool waiting = false;

View file

@ -226,7 +226,7 @@ void MediaPreviewWidget::setupLottie() {
Expects(_document != nullptr);
_lottie = std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(_document->data(), _document->filepath()),
Lottie::ReadContent(_documentMedia->bytes(), _document->filepath()),
Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() },
Lottie::Quality::High);
@ -266,7 +266,7 @@ QPixmap MediaPreviewWidget::currentImage() const {
if (_document->loaded()) {
if (!_gif && !_gif.isBad()) {
auto that = const_cast<MediaPreviewWidget*>(this);
that->_gif = Media::Clip::MakeReader(_document, FullMsgId(), [=](Media::Clip::Notification notification) {
that->_gif = Media::Clip::MakeReader(_documentMedia.get(), FullMsgId(), [=](Media::Clip::Notification notification) {
that->clipCallback(notification);
});
if (_gif) _gif->setAutoplay();