Added ability to generate single media preview from history item.

This commit is contained in:
23rd 2021-07-11 17:27:00 +03:00 committed by John Preston
parent 86db29cec7
commit c1e86418c2
3 changed files with 286 additions and 0 deletions

View File

@ -1071,6 +1071,8 @@ PRIVATE
support/support_templates.h
ui/chat/attach/attach_item_single_file_preview.cpp
ui/chat/attach/attach_item_single_file_preview.h
ui/chat/attach/attach_item_single_media_preview.cpp
ui/chat/attach/attach_item_single_media_preview.h
ui/effects/fireworks_animation.cpp
ui/effects/fireworks_animation.h
ui/effects/round_checkbox.cpp

View File

@ -0,0 +1,216 @@
/*
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 "ui/chat/attach/attach_item_single_media_preview.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 "data/data_streaming.h"
#include "history/history_item.h"
#include "history/view/media/history_view_document.h"
#include "main/main_session.h"
#include "media/streaming/media_streaming_document.h"
#include "media/streaming/media_streaming_instance.h"
#include "media/streaming/media_streaming_loader_local.h"
#include "media/streaming/media_streaming_player.h"
namespace Ui {
namespace {
using namespace ::Media::Streaming;
} // namespace
ItemSingleMediaPreview::ItemSingleMediaPreview(
QWidget *parent,
Fn<bool()> gifPaused,
not_null<HistoryItem*> item)
: AbstractSingleMediaPreview(parent)
, _gifPaused(std::move(gifPaused))
, _fullId(item->fullId()) {
const auto media = item->media();
Assert(media != nullptr);
Main::Session *session = nullptr;
if (const auto photo = media->photo()) {
_photoMedia = photo->createMediaView();
_photoMedia->wanted(Data::PhotoSize::Large, item->fullId());
session = &photo->session();
} else if (const auto document = media->document()) {
_documentMedia = document->createMediaView();
_documentMedia->thumbnailWanted(item->fullId());
session = &document->session();
if (document->isAnimation() || document->isVideoFile()) {
setAnimated(true);
prepareStreamedPreview();
}
} else {
Unexpected("Photo or document should be set.");
}
struct ThumbInfo {
bool loaded = false;
Image *image = nullptr;
};
const auto computeThumbInfo = [=]() -> ThumbInfo {
using Size = Data::PhotoSize;
if (_documentMedia) {
return { true, _documentMedia->thumbnail() };
} else if (const auto large = _photoMedia->image(Size::Large)) {
return { true, large };
} else if (const auto thumbnail = _photoMedia->image(
Size::Thumbnail)) {
return { false, thumbnail };
} else if (const auto small = _photoMedia->image(Size::Small)) {
return { false, small };
} else {
return { false, _photoMedia->thumbnailInline() };
}
};
rpl::single(
rpl::empty_value()
) | rpl::then(
session->downloaderTaskFinished()
) | rpl::start_with_next([=] {
const auto computed = computeThumbInfo();
if (computed.loaded) {
_lifetimeDownload.destroy();
}
preparePreview(computed.image->original());
}, _lifetimeDownload);
}
void ItemSingleMediaPreview::prepareStreamedPreview() {
if (_streamed || !_documentMedia) {
return;
}
const auto document = _documentMedia
? _documentMedia->owner().get()
: nullptr;
if (document && document->isAnimation()) {
setupStreamedPreview(
document->owner().streaming().sharedDocument(
document,
_fullId));
}
}
void ItemSingleMediaPreview::setupStreamedPreview(
std::shared_ptr<Document> shared) {
if (!shared) {
return;
}
_streamed = std::make_unique<Instance>(
std::move(shared),
[=] { update(); });
_streamed->lockPlayer();
_streamed->player().updates(
) | rpl::start_with_next_error([=](Update &&update) {
handleStreamingUpdate(std::move(update));
}, [=](Error &&error) {
handleStreamingError(std::move(error));
}, _streamed->lifetime());
if (_streamed->ready()) {
streamingReady(base::duplicate(_streamed->info()));
}
checkStreamedIsStarted();
}
void ItemSingleMediaPreview::handleStreamingUpdate(Update &&update) {
v::match(update.data, [&](Information &update) {
streamingReady(std::move(update));
}, [&](const PreloadedVideo &update) {
}, [&](const UpdateVideo &update) {
this->update();
}, [&](const PreloadedAudio &update) {
}, [&](const UpdateAudio &update) {
}, [&](const WaitingForData &update) {
}, [&](MutedByOther) {
}, [&](Finished) {
});
}
void ItemSingleMediaPreview::handleStreamingError(Error &&error) {
}
void ItemSingleMediaPreview::streamingReady(Information &&info) {
}
void ItemSingleMediaPreview::checkStreamedIsStarted() {
if (!_streamed) {
return;
} else if (_streamed->paused()) {
_streamed->resume();
}
if (!_streamed->active() && !_streamed->failed()) {
startStreamedPlayer();
}
}
void ItemSingleMediaPreview::startStreamedPlayer() {
auto options = ::Media::Streaming::PlaybackOptions();
options.audioId = _documentMedia
? AudioMsgId(_documentMedia->owner(), _fullId)
: AudioMsgId();
options.waitForMarkAsShown = true;
//if (!_streamed->withSound) {
options.mode = ::Media::Streaming::Mode::Video;
options.loop = true;
//}
_streamed->play(options);
}
bool ItemSingleMediaPreview::drawBackground() const {
return true; // A sticker can't be here.
}
bool ItemSingleMediaPreview::tryPaintAnimation(Painter &p) {
checkStreamedIsStarted();
if (_streamed
&& _streamed->player().ready()
&& !_streamed->player().videoSize().isEmpty()) {
const auto s = QSize(previewWidth(), previewHeight());
const auto paused = _gifPaused();
auto request = ::Media::Streaming::FrameRequest();
request.outer = s * cIntRetinaFactor();
request.resize = s * cIntRetinaFactor();
p.drawImage(
QRect(
previewLeft(),
previewTop(),
previewWidth(),
previewHeight()),
_streamed->frame(request));
if (!paused) {
_streamed->markFrameShown();
}
return true;
}
return false;
}
bool ItemSingleMediaPreview::isAnimatedPreviewReady() const {
return _streamed != nullptr;
}
auto ItemSingleMediaPreview::sharedPhotoMedia() const
-> std::shared_ptr<::Data::PhotoMedia> {
return _photoMedia;
}
} // namespace Ui

View File

@ -0,0 +1,68 @@
/*
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
*/
#pragma once
#include "ui/chat/attach/attach_abstract_single_media_preview.h"
namespace Data {
class DocumentMedia;
class PhotoMedia;
} // namespace Data
namespace Media {
namespace Streaming {
class Instance;
class Document;
struct Update;
enum class Error;
struct Information;
} // namespace Streaming
} // namespace Media
class HistoryItem;
namespace Ui {
class ItemSingleMediaPreview final : public AbstractSingleMediaPreview {
public:
ItemSingleMediaPreview(
QWidget *parent,
Fn<bool()> gifPaused,
not_null<HistoryItem*> item);
std::shared_ptr<::Data::PhotoMedia> sharedPhotoMedia() const;
protected:
bool drawBackground() const override;
bool tryPaintAnimation(Painter &p) override;
bool isAnimatedPreviewReady() const override;
private:
void prepareStreamedPreview();
void checkStreamedIsStarted();
void setupStreamedPreview(
std::shared_ptr<::Media::Streaming::Document> shared);
void handleStreamingUpdate(::Media::Streaming::Update &&update);
void handleStreamingError(::Media::Streaming::Error &&error);
void streamingReady(::Media::Streaming::Information &&info);
void startStreamedPlayer();
const Fn<bool()> _gifPaused;
const FullMsgId _fullId;
std::shared_ptr<::Data::PhotoMedia> _photoMedia;
std::shared_ptr<::Data::DocumentMedia> _documentMedia;
std::unique_ptr<::Media::Streaming::Instance> _streamed;
rpl::lifetime _lifetimeDownload;
};
} // namespace Ui