Load and show video thumbnails in the panel.

This commit is contained in:
John Preston 2020-05-22 15:15:09 +04:00
parent 33c1c48ad9
commit 3c9ca2eb94
11 changed files with 206 additions and 17 deletions

View File

@ -606,7 +606,8 @@ bool DocumentData::checkWallPaperProperties() {
void DocumentData::updateThumbnails(
const QByteArray &inlineThumbnailBytes,
const ImageWithLocation &thumbnail) {
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail) {
if (!inlineThumbnailBytes.isEmpty()
&& _inlineThumbnailBytes.isEmpty()) {
_inlineThumbnailBytes = inlineThumbnailBytes;
@ -636,10 +637,16 @@ void DocumentData::updateThumbnails(
}
}
}
}
const ImageLocation &DocumentData::thumbnailLocation() const {
return _thumbnailLocation;
if (videoThumbnail.location.valid()
&& !_videoThumbnailLocation.valid()) {
_videoThumbnailLocation = videoThumbnail.location;
_videoThumbnailByteSize = videoThumbnail.bytesCount;
if (_videoThumbnailLoader) {
const auto origin
= base::take(_videoThumbnailLoader)->fileOrigin();
loadVideoThumbnail(origin);
}
}
}
bool DocumentData::isWallPaper() const {
@ -702,6 +709,67 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) {
_thumbnailLoader->start();
}
const ImageLocation &DocumentData::thumbnailLocation() const {
return _thumbnailLocation;
}
bool DocumentData::hasVideoThumbnail() const {
return _videoThumbnailLocation.valid()
&& (_videoThumbnailLocation.width() > 0)
&& (_videoThumbnailLocation.height() > 0);
}
bool DocumentData::videoThumbnailLoading() const {
return _videoThumbnailLoader != nullptr;
}
bool DocumentData::videoThumbnailFailed() const {
return (_flags & Flag::VideoThumbnailFailed);
}
void DocumentData::loadVideoThumbnail(Data::FileOrigin origin) {
if (_videoThumbnailLoader || (_flags & Flag::VideoThumbnailFailed)) {
return;
} else if (const auto active = activeMediaView()) {
if (!active->videoThumbnailContent().isEmpty()) {
return;
}
}
const auto autoLoading = false;
_videoThumbnailLoader = CreateFileLoader(
_videoThumbnailLocation.file(),
origin,
QString(),
_videoThumbnailByteSize,
UnknownFileLocation,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
autoLoading,
Data::kAnimationCacheTag);
_videoThumbnailLoader->updates(
) | rpl::start_with_error_done([=](bool started) {
_videoThumbnailLoader = nullptr;
_flags |= Flag::VideoThumbnailFailed;
}, [=] {
if (_videoThumbnailLoader && !_videoThumbnailLoader->cancelled()) {
auto bytes = _videoThumbnailLoader->bytes();
if (bytes.isEmpty()) {
_flags |= Flag::VideoThumbnailFailed;
} else if (const auto active = activeMediaView()) {
active->setVideoThumbnail(std::move(bytes));
}
}
_videoThumbnailLoader = nullptr;
}, _videoThumbnailLoader->lifetime());
_videoThumbnailLoader->start();
}
const ImageLocation &DocumentData::videoThumbnailLocation() const {
return _videoThumbnailLocation;
}
Storage::Cache::Key DocumentData::goodThumbnailCacheKey() const {
return Data::DocumentThumbCacheKey(_dc, id);
}

View File

@ -159,10 +159,18 @@ public:
[[nodiscard]] bool thumbnailLoading() const;
[[nodiscard]] bool thumbnailFailed() const;
void loadThumbnail(Data::FileOrigin origin);
const ImageLocation &thumbnailLocation() const;
[[nodiscard]] bool hasVideoThumbnail() const;
[[nodiscard]] bool videoThumbnailLoading() const;
[[nodiscard]] bool videoThumbnailFailed() const;
void loadVideoThumbnail(Data::FileOrigin origin);
const ImageLocation &videoThumbnailLocation() const;
void updateThumbnails(
const QByteArray &inlineThumbnailBytes,
const ImageWithLocation &thumbnail);
const ImageLocation &thumbnailLocation() const;
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail);
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
return _inlineThumbnailBytes;
@ -247,6 +255,7 @@ private:
DownloadCancelled = 0x10,
LoadedInMediaCache = 0x20,
ThumbnailFailed = 0x40,
VideoThumbnailFailed = 0x80,
};
using Flags = base::flags<Flag>;
friend constexpr bool is_flag_type(Flag) { return true; };
@ -300,8 +309,11 @@ private:
QByteArray _inlineThumbnailBytes;
ImageLocation _thumbnailLocation;
ImageLocation _videoThumbnailLocation;
std::unique_ptr<FileLoader> _thumbnailLoader;
std::unique_ptr<FileLoader> _videoThumbnailLoader;
int _thumbnailByteSize = 0;
int _videoThumbnailByteSize = 0;
std::unique_ptr<Data::ReplyPreview> _replyPreview;
std::weak_ptr<Data::DocumentMedia> _media;
PhotoData *_goodThumbnailPhoto = nullptr;

View File

@ -154,6 +154,25 @@ void DocumentMedia::setThumbnail(QImage thumbnail) {
_owner->session().downloaderTaskFinished().notify();
}
QByteArray DocumentMedia::videoThumbnailContent() const {
return _videoThumbnailBytes;
}
QSize DocumentMedia::videoThumbnailSize() const {
const auto &location = _owner->videoThumbnailLocation();
return { location.width(), location.height() };
}
void DocumentMedia::videoThumbnailWanted(Data::FileOrigin origin) {
if (_videoThumbnailBytes.isEmpty()) {
_owner->loadVideoThumbnail(origin);
}
}
void DocumentMedia::setVideoThumbnail(QByteArray content) {
_videoThumbnailBytes = std::move(content);
}
void DocumentMedia::checkStickerLarge() {
if (_sticker) {
return;

View File

@ -25,11 +25,17 @@ public:
void setGoodThumbnail(QImage thumbnail);
[[nodiscard]] Image *thumbnailInline() const;
[[nodiscard]] Image *thumbnail() const;
[[nodiscard]] QSize thumbnailSize() const;
void thumbnailWanted(Data::FileOrigin origin);
void setThumbnail(QImage thumbnail);
[[nodiscard]] QByteArray videoThumbnailContent() const;
[[nodiscard]] QSize videoThumbnailSize() const;
void videoThumbnailWanted(Data::FileOrigin origin);
void setVideoThumbnail(QByteArray content);
void checkStickerLarge();
void checkStickerSmall();
[[nodiscard]] Image *getStickerSmall();
@ -67,6 +73,7 @@ private:
std::unique_ptr<Image> _thumbnail;
std::unique_ptr<Image> _sticker;
QByteArray _bytes;
QByteArray _videoThumbnailBytes;
Flags _flags;
};

View File

@ -165,6 +165,25 @@ MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) {
: MTPPhotoSize(MTP_photoSizeEmpty(MTP_string()));
}
std::optional<MTPVideoSize> FindDocumentVideoThumbnail(
const MTPDdocument &data) {
const auto area = [](const MTPVideoSize &size) {
static constexpr auto kInvalid = 0;
return size.match([](const MTPDvideoSize &data) {
return (data.vw().v * data.vh().v);
});
};
const auto thumbs = data.vvideo_thumbs();
if (!thumbs) {
return std::nullopt;
}
const auto &list = thumbs->v;
const auto i = ranges::max_element(list, std::less<>(), area);
return (i != list.end() && area(*i) > 0)
? std::make_optional(*i)
: std::nullopt;
}
rpl::producer<int> PinnedDialogsCountMaxValue(
not_null<Main::Session*> session) {
return rpl::single(
@ -2382,6 +2401,7 @@ not_null<DocumentData*> Session::processDocument(
qs(data.vmime_type()),
QByteArray(),
thumbnail,
ImageWithLocation(),
data.vdc_id().v,
data.vsize().v);
}, [&](const MTPDdocumentEmpty &data) {
@ -2398,6 +2418,7 @@ not_null<DocumentData*> Session::document(
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
int32 size) {
const auto result = document(id);
@ -2410,6 +2431,7 @@ not_null<DocumentData*> Session::document(
mime,
inlineThumbnailBytes,
thumbnail,
videoThumbnail,
dc,
size);
return result;
@ -2478,6 +2500,7 @@ DocumentData *Session::documentFromWeb(
data.vmime_type().v,
QByteArray(),
ImageWithLocation{ .location = thumbnailLocation },
ImageWithLocation(),
MTP::maindc(),
int32(0)); // data.vsize().v
result->setWebLocation(WebFileLocation(
@ -2498,6 +2521,7 @@ DocumentData *Session::documentFromWeb(
data.vmime_type().v,
QByteArray(),
ImageWithLocation{ .location = thumbnailLocation },
ImageWithLocation(),
MTP::maindc(),
int32(0)); // data.vsize().v
result->setContentUrl(qs(data.vurl()));
@ -2517,10 +2541,14 @@ void Session::documentApplyFields(
const MTPDdocument &data) {
const auto inlineThumbnailBytes = FindDocumentInlineThumbnail(data);
const auto thumbnailSize = FindDocumentThumbnail(data);
const auto videoThumbnailSize = FindDocumentVideoThumbnail(data);
const auto prepared = Images::FromPhotoSize(
_session,
data,
thumbnailSize);
const auto videoThumbnail = videoThumbnailSize
? Images::FromVideoSize(_session, data, *videoThumbnailSize)
: ImageWithLocation();
documentApplyFields(
document,
data.vaccess_hash().v,
@ -2530,6 +2558,7 @@ void Session::documentApplyFields(
qs(data.vmime_type()),
inlineThumbnailBytes,
prepared,
videoThumbnail,
data.vdc_id().v,
data.vsize().v);
}
@ -2543,6 +2572,7 @@ void Session::documentApplyFields(
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
int32 size) {
if (!date) {
@ -2550,7 +2580,10 @@ void Session::documentApplyFields(
}
document->date = date;
document->setMimeString(mime);
document->updateThumbnails(inlineThumbnailBytes, thumbnail);
document->updateThumbnails(
inlineThumbnailBytes,
thumbnail,
videoThumbnail);
document->size = size;
document->setattributes(attributes);

View File

@ -500,6 +500,7 @@ public:
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
int32 size);
void documentConvert(
@ -754,6 +755,7 @@ private:
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const ImageWithLocation &thumbnail,
const ImageWithLocation &videoThumbnail,
int32 dc,
int32 size);
DocumentData *documentFromWeb(

View File

@ -134,26 +134,41 @@ int Gif::resizeGetHeight(int width) {
void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
const auto document = getShownDocument();
const auto displayLoading = document->displayLoading();
const auto useVideoThumbnail = document->hasVideoThumbnail();
ensureDataMediaCreated(document);
_dataMedia->automaticLoad(fileOrigin(), nullptr);
if (!useVideoThumbnail) {
_dataMedia->automaticLoad(fileOrigin(), nullptr);
}
bool loaded = _dataMedia->loaded(), loading = document->loading();
const auto loaded = useVideoThumbnail
? !_dataMedia->videoThumbnailContent().isEmpty()
: _dataMedia->loaded();
const auto loading = useVideoThumbnail
? document->videoThumbnailLoading()
: document->loading();
if (loaded
&& !_gif
&& !_gif.isBad()
&& CanPlayInline(document)) {
auto that = const_cast<Gif*>(this);
that->_gif = Media::Clip::MakeReader(_dataMedia.get(), FullMsgId(), [that](Media::Clip::Notification notification) {
const auto callback = [=](Media::Clip::Notification notification) {
that->clipCallback(notification);
});
};
that->_gif = useVideoThumbnail
? Media::Clip::MakeReader(
_dataMedia->videoThumbnailContent(),
callback)
: Media::Clip::MakeReader(
_dataMedia.get(),
FullMsgId(),
callback);
}
const auto animating = (_gif && _gif->started());
if (displayLoading) {
ensureAnimation();
if (!_animation->radial.animating()) {
_animation->radial.start(_dataMedia->progress());
_animation->radial.start(_dataMedia->progress()); // #TODO video_thumbs
}
}
const auto radial = isRadialAnimation();
@ -177,6 +192,10 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
p.drawPixmap(r.topLeft(), _thumb);
}
}
if (useVideoThumbnail) {
AssertIsDebug();
p.fillRect(QRect(r.topLeft(), QSize(20, 20)), Qt::green);
}
if (radial || _gif.isBad() || (!_gif && !loaded && !loading)) {
auto radialOpacity = (radial && loaded) ? _animation->radial.opacity() : 1.;
@ -250,7 +269,7 @@ void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
bool wasactive = (_state & StateFlag::Over);
if (active != wasactive) {
ensureDataMediaCreated(getShownDocument());
if (!_dataMedia->loaded()) {
if (!_dataMedia->loaded()) { // #TODO video_thumbs
ensureAnimation();
auto from = active ? 0. : 1., to = active ? 1. : 0.;
_animation->_a_over.start([this] { update(); }, from, to, st::stickersRowDuration);
@ -333,6 +352,7 @@ void Gif::ensureDataMediaCreated(not_null<DocumentData*> document) const {
}
_dataMedia = document->createMediaView();
_dataMedia->thumbnailWanted(fileOrigin());
_dataMedia->videoThumbnailWanted(fileOrigin());
}
void Gif::ensureAnimation() const {
@ -349,7 +369,7 @@ bool Gif::isRadialAnimation() const {
return true;
} else {
ensureDataMediaCreated(getShownDocument());
if (_dataMedia->loaded()) {
if (_dataMedia->loaded()) { // #TODO video_thumbs
_animation = nullptr;
}
}
@ -362,7 +382,7 @@ void Gif::radialAnimationCallback(crl::time now) const {
ensureDataMediaCreated(document);
const auto updated = [&] {
return _animation->radial.update(
_dataMedia->progress(),
_dataMedia->progress(), // #TODO video_thumbs
!document->loading() || _dataMedia->loaded(),
now);
}();

View File

@ -3945,6 +3945,7 @@ void importOldRecentStickers() {
mime,
QByteArray(),
ImageWithLocation(),
ImageWithLocation(),
dc,
size);
if (!doc->sticker()) {

View File

@ -143,6 +143,7 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
mime,
QByteArray(),
ImageWithLocation{ .location = *thumb },
ImageWithLocation(),
dc,
size);
}

View File

@ -121,4 +121,26 @@ ImageLocation FromWebDocument(const MTPWebDocument &document) {
});
}
ImageWithLocation FromVideoSize(
not_null<Main::Session*> session,
const MTPDdocument &document,
const MTPVideoSize &size) {
return size.match([&](const MTPDvideoSize &data) {
return ImageWithLocation{
.location = ImageLocation(
DownloadLocation{ StorageFileLocation(
document.vdc_id().v,
session->userId(),
MTP_inputDocumentFileLocation(
document.vid(),
document.vaccess_hash(),
document.vfile_reference(),
data.vtype())) },
data.vw().v,
data.vh().v),
.bytesCount = data.vsize().v
};
});
}
} // namespace Images

View File

@ -19,6 +19,10 @@ namespace Images {
not_null<Main::Session*> session,
const MTPDdocument &document,
const MTPPhotoSize &size);
[[nodiscard]] ImageWithLocation FromVideoSize(
not_null<Main::Session*> session,
const MTPDdocument &document,
const MTPVideoSize &size);
[[nodiscard]] ImageWithLocation FromImageInMemory(
const QImage &image,
const char *format);