Improve video userpics in chat history.
This commit is contained in:
parent
f99960e1f6
commit
5c5414b680
|
@ -309,6 +309,18 @@ bool ShouldAutoPlay(
|
||||||
document->size);
|
document->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShouldAutoPlay(
|
||||||
|
const Full &data,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
not_null<PhotoData*> photo) {
|
||||||
|
const auto source = SourceFromPeer(peer);
|
||||||
|
const auto size = photo->videoByteSize();
|
||||||
|
return photo->hasVideo()
|
||||||
|
&& (data.shouldDownload(source, Type::AutoPlayGIF, size)
|
||||||
|
|| data.shouldDownload(source, Type::AutoPlayVideo, size)
|
||||||
|
|| data.shouldDownload(source, Type::AutoPlayVideoMessage, size));
|
||||||
|
}
|
||||||
|
|
||||||
Full WithDisabledAutoPlay(const Full &data) {
|
Full WithDisabledAutoPlay(const Full &data) {
|
||||||
auto result = data;
|
auto result = data;
|
||||||
for (const auto source : enums_view<Source>(kSourcesCount)) {
|
for (const auto source : enums_view<Source>(kSourcesCount)) {
|
||||||
|
|
|
@ -120,6 +120,10 @@ private:
|
||||||
const Full &data,
|
const Full &data,
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
not_null<DocumentData*> document);
|
not_null<DocumentData*> document);
|
||||||
|
[[nodiscard]] bool ShouldAutoPlay(
|
||||||
|
const Full &data,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
not_null<PhotoData*> photo);
|
||||||
|
|
||||||
[[nodiscard]] Full WithDisabledAutoPlay(const Full &data);
|
[[nodiscard]] Full WithDisabledAutoPlay(const Full &data);
|
||||||
|
|
||||||
|
|
|
@ -3208,6 +3208,10 @@ void Session::checkPlayingAnimations() {
|
||||||
if (document->isAnimation() || document->isVideoFile()) {
|
if (document->isAnimation() || document->isVideoFile()) {
|
||||||
check.emplace(view);
|
check.emplace(view);
|
||||||
}
|
}
|
||||||
|
} else if (const auto photo = media->getPhoto()) {
|
||||||
|
if (photo->hasVideo()) {
|
||||||
|
check.emplace(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "media/streaming/media_streaming_player.h"
|
#include "media/streaming/media_streaming_player.h"
|
||||||
#include "media/streaming/media_streaming_document.h"
|
#include "media/streaming/media_streaming_document.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
#include "main/main_session_settings.h"
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
#include "ui/grouped_layout.h"
|
#include "ui/grouped_layout.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
@ -25,6 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_photo_media.h"
|
#include "data/data_photo_media.h"
|
||||||
#include "data/data_file_origin.h"
|
#include "data/data_file_origin.h"
|
||||||
|
#include "data/data_auto_download.h"
|
||||||
|
#include "core/application.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "styles/style_history.h"
|
#include "styles/style_history.h"
|
||||||
|
|
||||||
|
@ -35,6 +38,17 @@ using Data::PhotoSize;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
struct Photo::Streamed {
|
||||||
|
explicit Streamed(std::shared_ptr<::Media::Streaming::Document> shared);
|
||||||
|
::Media::Streaming::Instance instance;
|
||||||
|
QImage frozenFrame;
|
||||||
|
};
|
||||||
|
|
||||||
|
Photo::Streamed::Streamed(
|
||||||
|
std::shared_ptr<::Media::Streaming::Document> shared)
|
||||||
|
: instance(std::move(shared), nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
Photo::Photo(
|
Photo::Photo(
|
||||||
not_null<Element*> parent,
|
not_null<Element*> parent,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
|
@ -61,7 +75,7 @@ Photo::~Photo() {
|
||||||
if (_streamed || _dataMedia) {
|
if (_streamed || _dataMedia) {
|
||||||
if (_streamed) {
|
if (_streamed) {
|
||||||
_data->owner().streaming().keepAlive(_data);
|
_data->owner().streaming().keepAlive(_data);
|
||||||
setStreamed(nullptr);
|
stopAnimation();
|
||||||
}
|
}
|
||||||
if (_dataMedia) {
|
if (_dataMedia) {
|
||||||
_data->owner().keepAlive(base::take(_dataMedia));
|
_data->owner().keepAlive(base::take(_dataMedia));
|
||||||
|
@ -107,7 +121,7 @@ bool Photo::hasHeavyPart() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::unloadHeavyPart() {
|
void Photo::unloadHeavyPart() {
|
||||||
setStreamed(nullptr);
|
stopAnimation();
|
||||||
_dataMedia = nullptr;
|
_dataMedia = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +234,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time
|
||||||
|
|
||||||
auto rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
|
auto rthumb = style::rtlrect(paintx, painty, paintw, painth, width());
|
||||||
if (_serviceWidth > 0) {
|
if (_serviceWidth > 0) {
|
||||||
paintUserpicFrame(p, rthumb.topLeft());
|
paintUserpicFrame(p, rthumb.topLeft(), selected);
|
||||||
} else {
|
} else {
|
||||||
if (bubble) {
|
if (bubble) {
|
||||||
if (!_caption.isEmpty()) {
|
if (!_caption.isEmpty()) {
|
||||||
|
@ -316,21 +330,40 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::paintUserpicFrame(Painter &p, QPoint photoPosition) const {
|
void Photo::paintUserpicFrame(
|
||||||
const_cast<Photo*>(this)->validateVideo();
|
Painter &p,
|
||||||
|
QPoint photoPosition,
|
||||||
|
bool selected) const {
|
||||||
|
const auto autoplay = _data->videoCanBePlayed() && videoAutoplayEnabled();
|
||||||
|
const auto startPlay = autoplay && !_streamed;
|
||||||
|
if (startPlay) {
|
||||||
|
const_cast<Photo*>(this)->playAnimation(true);
|
||||||
|
} else {
|
||||||
|
checkStreamedIsStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = QSize{ _pixw, _pixh };
|
||||||
|
const auto rect = QRect(photoPosition, size);
|
||||||
|
|
||||||
if (_streamed
|
if (_streamed
|
||||||
&& _streamed->player().ready()
|
&& _streamed->instance.player().ready()
|
||||||
&& !_streamed->player().videoSize().isEmpty()) {
|
&& !_streamed->instance.player().videoSize().isEmpty()) {
|
||||||
const auto paused = _parent->delegate()->elementIsGifPaused();
|
const auto paused = _parent->delegate()->elementIsGifPaused();
|
||||||
auto request = ::Media::Streaming::FrameRequest();
|
auto request = ::Media::Streaming::FrameRequest();
|
||||||
auto size = QSize{ _pixw, _pixh };
|
|
||||||
request.outer = size * cIntRetinaFactor();
|
request.outer = size * cIntRetinaFactor();
|
||||||
request.resize = size * cIntRetinaFactor();
|
request.resize = size * cIntRetinaFactor();
|
||||||
request.radius = ImageRoundRadius::Ellipse;
|
request.radius = ImageRoundRadius::Ellipse;
|
||||||
p.drawImage(QRect(photoPosition, size), _streamed->frame(request));
|
if (_streamed->instance.playerLocked()) {
|
||||||
if (!paused) {
|
if (_streamed->frozenFrame.isNull()) {
|
||||||
_streamed->markFrameShown();
|
_streamed->frozenFrame = _streamed->instance.frame(request);
|
||||||
|
}
|
||||||
|
p.drawImage(rect, _streamed->frozenFrame);
|
||||||
|
} else {
|
||||||
|
_streamed->frozenFrame = QImage();
|
||||||
|
p.drawImage(rect, _streamed->instance.frame(request));
|
||||||
|
if (!paused) {
|
||||||
|
_streamed->instance.markFrameShown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -349,7 +382,28 @@ void Photo::paintUserpicFrame(Painter &p, QPoint photoPosition) const {
|
||||||
return QPixmap();
|
return QPixmap();
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
p.drawPixmap(photoPosition, pix);
|
p.drawPixmap(rect, pix);
|
||||||
|
|
||||||
|
if (_data->videoCanBePlayed() && !_streamed) {
|
||||||
|
auto inner = QRect(rect.x() + (rect.width() - st::msgFileSize) / 2, rect.y() + (rect.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
if (selected) {
|
||||||
|
p.setBrush(st::msgDateImgBgSelected);
|
||||||
|
} else {
|
||||||
|
const auto over = ClickHandler::showAsActive(_openl);
|
||||||
|
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
PainterHighQualityEnabler hq(p);
|
||||||
|
p.drawEllipse(inner);
|
||||||
|
}
|
||||||
|
const auto icon = [&]() -> const style::icon * {
|
||||||
|
return &(selected ? st::historyFileThumbPlaySelected : st::historyFileThumbPlay);
|
||||||
|
}();
|
||||||
|
if (icon) {
|
||||||
|
icon->paintInCenter(p, inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextState Photo::textState(QPoint point, StateRequest request) const {
|
TextState Photo::textState(QPoint point, StateRequest request) const {
|
||||||
|
@ -615,28 +669,28 @@ void Photo::validateGroupedCache(
|
||||||
bool Photo::createStreamingObjects() {
|
bool Photo::createStreamingObjects() {
|
||||||
using namespace ::Media::Streaming;
|
using namespace ::Media::Streaming;
|
||||||
|
|
||||||
setStreamed(std::make_unique<Instance>(
|
setStreamed(std::make_unique<Streamed>(
|
||||||
history()->owner().streaming().sharedDocument(
|
history()->owner().streaming().sharedDocument(
|
||||||
_data,
|
_data,
|
||||||
_realParent->fullId()),
|
_realParent->fullId())));
|
||||||
nullptr));
|
_streamed->instance.player().updates(
|
||||||
_streamed->player().updates(
|
|
||||||
) | rpl::start_with_next_error([=](Update &&update) {
|
) | rpl::start_with_next_error([=](Update &&update) {
|
||||||
handleStreamingUpdate(std::move(update));
|
handleStreamingUpdate(std::move(update));
|
||||||
}, [=](Error &&error) {
|
}, [=](Error &&error) {
|
||||||
handleStreamingError(std::move(error));
|
handleStreamingError(std::move(error));
|
||||||
}, _streamed->lifetime());
|
}, _streamed->instance.lifetime());
|
||||||
if (_streamed->ready()) {
|
if (_streamed->instance.ready()) {
|
||||||
streamingReady(base::duplicate(_streamed->info()));
|
streamingReady(base::duplicate(_streamed->instance.info()));
|
||||||
}
|
}
|
||||||
if (!_streamed->valid()) {
|
if (!_streamed->instance.valid()) {
|
||||||
setStreamed(nullptr);
|
stopAnimation();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
checkStreamedIsStarted();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::setStreamed(std::unique_ptr<::Media::Streaming::Instance> value) {
|
void Photo::setStreamed(std::unique_ptr<Streamed> value) {
|
||||||
const auto removed = (_streamed && !value);
|
const auto removed = (_streamed && !value);
|
||||||
const auto set = (!_streamed && value);
|
const auto set = (!_streamed && value);
|
||||||
_streamed = std::move(value);
|
_streamed = std::move(value);
|
||||||
|
@ -665,14 +719,13 @@ void Photo::handleStreamingUpdate(::Media::Streaming::Update &&update) {
|
||||||
|
|
||||||
void Photo::handleStreamingError(::Media::Streaming::Error &&error) {
|
void Photo::handleStreamingError(::Media::Streaming::Error &&error) {
|
||||||
_data->setVideoPlaybackFailed();
|
_data->setVideoPlaybackFailed();
|
||||||
setStreamed(nullptr);
|
stopAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::repaintStreamedContent() {
|
void Photo::repaintStreamedContent() {
|
||||||
/* const auto own = activeOwnStreamed();
|
if (_streamed && !_streamed->frozenFrame.isNull()) {
|
||||||
if (own && !own->frozenFrame.isNull()) {
|
return;
|
||||||
return;
|
} else if (_parent->delegate()->elementIsGifPaused()) {
|
||||||
} else */if (_parent->delegate()->elementIsGifPaused()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
history()->owner().requestViewRepaint(_parent);
|
history()->owner().requestViewRepaint(_parent);
|
||||||
|
@ -682,36 +735,62 @@ void Photo::streamingReady(::Media::Streaming::Information &&info) {
|
||||||
history()->owner().requestViewRepaint(_parent);
|
history()->owner().requestViewRepaint(_parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::validateVideo() {
|
void Photo::checkAnimation() {
|
||||||
if (!_data->videoCanBePlayed()) {
|
if (_streamed && !videoAutoplayEnabled()) {
|
||||||
setStreamed(nullptr);
|
stopAnimation();
|
||||||
return;
|
|
||||||
} else if (_streamed) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!createStreamingObjects()) {
|
|
||||||
_data->setVideoPlaybackFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
checkStreamedIsStarted();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Photo::checkStreamedIsStarted() {
|
void Photo::stopAnimation() {
|
||||||
|
setStreamed(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Photo::playAnimation(bool autoplay) {
|
||||||
|
ensureDataMediaCreated();
|
||||||
|
if (_streamed && autoplay) {
|
||||||
|
return;
|
||||||
|
} else if (_streamed && videoAutoplayEnabled()) {
|
||||||
|
Core::App().showPhoto(_data, _parent->data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_streamed) {
|
||||||
|
stopAnimation();
|
||||||
|
} else if (_data->videoCanBePlayed()) {
|
||||||
|
if (!videoAutoplayEnabled()) {
|
||||||
|
history()->owner().checkPlayingAnimations();
|
||||||
|
}
|
||||||
|
if (!createStreamingObjects()) {
|
||||||
|
_data->setVideoPlaybackFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Photo::checkStreamedIsStarted() const {
|
||||||
if (!_streamed) {
|
if (!_streamed) {
|
||||||
return;
|
return;
|
||||||
} else if (_streamed->paused()) {
|
} else if (_streamed->instance.paused()) {
|
||||||
_streamed->resume();
|
_streamed->instance.resume();
|
||||||
}
|
}
|
||||||
if (_streamed && !_streamed->active() && !_streamed->failed()) {
|
if (_streamed
|
||||||
|
&& !_streamed->instance.active()
|
||||||
|
&& !_streamed->instance.failed()) {
|
||||||
const auto position = _data->videoStartPosition();
|
const auto position = _data->videoStartPosition();
|
||||||
auto options = ::Media::Streaming::PlaybackOptions();
|
auto options = ::Media::Streaming::PlaybackOptions();
|
||||||
options.position = position;
|
options.position = position;
|
||||||
options.mode = ::Media::Streaming::Mode::Video;
|
options.mode = ::Media::Streaming::Mode::Video;
|
||||||
options.loop = true;
|
options.loop = true;
|
||||||
_streamed->play(options);
|
_streamed->instance.play(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Photo::videoAutoplayEnabled() const {
|
||||||
|
return Data::AutoDownload::ShouldAutoPlay(
|
||||||
|
_data->session().settings().autoDownload(),
|
||||||
|
_realParent->history()->peer,
|
||||||
|
_data);
|
||||||
|
}
|
||||||
|
|
||||||
TextForMimeData Photo::selectedText(TextSelection selection) const {
|
TextForMimeData Photo::selectedText(TextSelection selection) const {
|
||||||
return _caption.toTextForMimeData(selection);
|
return _caption.toTextForMimeData(selection);
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,8 +98,14 @@ protected:
|
||||||
bool dataLoaded() const override;
|
bool dataLoaded() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Streamed;
|
||||||
|
|
||||||
void create(FullMsgId contextId, PeerData *chat = nullptr);
|
void create(FullMsgId contextId, PeerData *chat = nullptr);
|
||||||
|
|
||||||
|
void playAnimation(bool autoplay) override;
|
||||||
|
void stopAnimation() override;
|
||||||
|
void checkAnimation() override;
|
||||||
|
|
||||||
void ensureDataMediaCreated() const;
|
void ensureDataMediaCreated() const;
|
||||||
void dataMediaCreated() const;
|
void dataMediaCreated() const;
|
||||||
|
|
||||||
|
@ -113,15 +119,18 @@ private:
|
||||||
not_null<uint64*> cacheKey,
|
not_null<uint64*> cacheKey,
|
||||||
not_null<QPixmap*> cache) const;
|
not_null<QPixmap*> cache) const;
|
||||||
|
|
||||||
void validateVideo();
|
bool videoAutoplayEnabled() const;
|
||||||
void setStreamed(std::unique_ptr<::Media::Streaming::Instance> value);
|
void setStreamed(std::unique_ptr<Streamed> value);
|
||||||
void repaintStreamedContent();
|
void repaintStreamedContent();
|
||||||
void checkStreamedIsStarted();
|
void checkStreamedIsStarted() const;
|
||||||
bool createStreamingObjects();
|
bool createStreamingObjects();
|
||||||
void handleStreamingUpdate(::Media::Streaming::Update &&update);
|
void handleStreamingUpdate(::Media::Streaming::Update &&update);
|
||||||
void handleStreamingError(::Media::Streaming::Error &&error);
|
void handleStreamingError(::Media::Streaming::Error &&error);
|
||||||
void streamingReady(::Media::Streaming::Information &&info);
|
void streamingReady(::Media::Streaming::Information &&info);
|
||||||
void paintUserpicFrame(Painter &p, QPoint photoPosition) const;
|
void paintUserpicFrame(
|
||||||
|
Painter &p,
|
||||||
|
QPoint photoPosition,
|
||||||
|
bool selected) const;
|
||||||
|
|
||||||
not_null<PhotoData*> _data;
|
not_null<PhotoData*> _data;
|
||||||
int _serviceWidth = 0;
|
int _serviceWidth = 0;
|
||||||
|
@ -129,7 +138,7 @@ private:
|
||||||
int _pixh = 1;
|
int _pixh = 1;
|
||||||
Ui::Text::String _caption;
|
Ui::Text::String _caption;
|
||||||
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
mutable std::shared_ptr<Data::PhotoMedia> _dataMedia;
|
||||||
mutable std::unique_ptr<::Media::Streaming::Instance> _streamed;
|
mutable std::unique_ptr<Streamed> _streamed;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -840,6 +840,7 @@ bool UserpicButton::createStreamingObjects(not_null<PhotoData*> photo) {
|
||||||
_streamed = std::make_unique<Instance>(
|
_streamed = std::make_unique<Instance>(
|
||||||
photo->owner().streaming().sharedDocument(photo, origin),
|
photo->owner().streaming().sharedDocument(photo, origin),
|
||||||
nullptr);
|
nullptr);
|
||||||
|
_streamed->lockPlayer();
|
||||||
_streamed->player().updates(
|
_streamed->player().updates(
|
||||||
) | rpl::start_with_next_error([=](Update &&update) {
|
) | rpl::start_with_next_error([=](Update &&update) {
|
||||||
handleStreamingUpdate(std::move(update));
|
handleStreamingUpdate(std::move(update));
|
||||||
|
|
Loading…
Reference in New Issue