Fix voice/video messages in chats/feed.

This commit is contained in:
John Preston 2018-01-21 17:49:42 +03:00
parent f9154c4ed0
commit 861ab85ca1
30 changed files with 730 additions and 394 deletions

View file

@ -18,7 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_media_types.h"
#include "window/window_controller.h"
#include "auth_session.h"
#include "mainwindow.h"
#include "messenger.h"
namespace {
@ -286,14 +288,14 @@ void DocumentOpenClickHandler::doOpen(
} else if (data->size < App::kImageSizeLimit) {
if (!data->data().isEmpty() && playAnimation) {
if (action == ActionOnLoadPlayInline && context) {
Auth().data().requestItemPlayInline(context);
Auth().data().requestAnimationPlayInline(context);
} else {
Messenger::Instance().showDocument(data, context);
}
} else if (location.accessEnable()) {
if (data->isAnimation() || QImageReader(location.name()).canRead()) {
if (action == ActionOnLoadPlayInline && context) {
Auth().data().requestItemPlayInline(context);
if (playAnimation || QImageReader(location.name()).canRead()) {
if (playAnimation && action == ActionOnLoadPlayInline && context) {
Auth().data().requestAnimationPlayInline(context);
} else {
Messenger::Instance().showDocument(data, context);
}
@ -592,7 +594,7 @@ void DocumentData::performActionOnLoad() {
} else if (playAnimation) {
if (loaded()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
Auth().data().requestItemPlayInline(item);
Auth().data().requestAnimationPlayInline(item);
} else {
Messenger::Instance().showDocument(this, item);
}
@ -610,11 +612,7 @@ void DocumentData::performActionOnLoad() {
Auth().data().markMediaRead(this);
} else if (loc.accessEnable()) {
if (showImage && QImageReader(loc.name()).canRead()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item) {
Auth().data().requestItemPlayInline(item);
} else {
Messenger::Instance().showDocument(this, item);
}
} else {
File::Launch(already);
}

View file

@ -208,6 +208,7 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemResizeRequest() const {
void Session::requestViewResize(not_null<ViewElement*> view) {
view->setPendingResize();
_viewResizeRequest.fire_copy(view);
notifyViewLayoutChange(view);
}
rpl::producer<not_null<ViewElement*>> Session::viewResizeRequest() const {
@ -225,12 +226,12 @@ rpl::producer<not_null<HistoryItem*>> Session::itemViewRefreshRequest() const {
return _itemViewRefreshRequest.events();
}
void Session::requestItemPlayInline(not_null<const HistoryItem*> item) {
_itemPlayInlineRequest.fire_copy(item);
void Session::requestAnimationPlayInline(not_null<HistoryItem*> item) {
_animationPlayInlineRequest.fire_copy(item);
}
rpl::producer<not_null<const HistoryItem*>> Session::itemPlayInlineRequest() const {
return _itemPlayInlineRequest.events();
rpl::producer<not_null<HistoryItem*>> Session::animationPlayInlineRequest() const {
return _animationPlayInlineRequest.events();
}
void Session::notifyItemRemoved(not_null<const HistoryItem*> item) {
@ -1326,9 +1327,7 @@ void Session::unregisterAutoplayAnimation(
void Session::stopAutoplayAnimations() {
for (const auto [reader, view] : base::take(_autoplayAnimations)) {
if (const auto media = view->media()) {
if (!media->isRoundVideoPlaying()) {
media->stopInline();
}
media->stopAnimation();
}
}
}

View file

@ -72,8 +72,8 @@ public:
rpl::producer<not_null<ViewElement*>> viewResizeRequest() const;
void requestItemViewRefresh(not_null<HistoryItem*> item);
rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const;
void requestItemPlayInline(not_null<const HistoryItem*> item);
rpl::producer<not_null<const HistoryItem*>> itemPlayInlineRequest() const;
void requestAnimationPlayInline(not_null<HistoryItem*> item);
rpl::producer<not_null<HistoryItem*>> animationPlayInlineRequest() const;
void notifyHistoryUnloaded(not_null<const History*> history);
rpl::producer<not_null<const History*>> historyUnloaded() const;
@ -445,7 +445,7 @@ private:
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
rpl::event_stream<not_null<ViewElement*>> _viewResizeRequest;
rpl::event_stream<not_null<HistoryItem*>> _itemViewRefreshRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemPlayInlineRequest;
rpl::event_stream<not_null<HistoryItem*>> _animationPlayInlineRequest;
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
rpl::event_stream<not_null<const History*>> _historyUnloaded;
rpl::event_stream<not_null<const History*>> _historyCleared;

View file

@ -239,16 +239,6 @@ bool isLayerShown() {
return false;
}
void autoplayMediaInlineAsync(const FullMsgId &msgId) {
if (auto main = App::main()) {
InvokeQueued(main, [msgId] {
if (const auto item = App::histItemById(msgId)) {
Auth().data().requestItemPlayInline(item);
}
});
}
}
void showPeerProfile(const PeerId &peer) {
if (auto window = App::wnd()) {
if (auto controller = window->controller()) {
@ -340,33 +330,6 @@ void historyMuteUpdated(History *history) {
if (MainWidget *m = App::main()) m->notify_historyMuteUpdated(history);
}
//void handlePendingHistoryUpdate() {
//for (const auto item : base::take(Global::RefPendingRepaintItems())) {
// Auth().data().requestItemRepaint(item);
// Start the video if it is waiting for that.
//if (item->pendingInitDimensions()) { // #TODO floating player video
// if (const auto media = item->getMedia()) {
// if (const auto reader = media->getClipReader()) {
// const auto startRequired = [&] {
// if (reader->started()) {
// return false;
// }
// using Mode = Media::Clip::Reader::Mode;
// return (reader->mode() == Mode::Video);
// };
// if (startRequired()) {
// const auto width = std::max(
// item->width(),
// st::historyMinimalWidth);
// item->resizeGetHeight(width);
// }
// }
// }
//}
//}
//}
void unreadCounterUpdated() {
Global::RefHandleUnreadCounterUpdate().call();
}

View file

@ -182,8 +182,6 @@ void hideLayer(anim::type animated = anim::type::normal);
void hideSettingsAndLayer(anim::type animated = anim::type::normal);
bool isLayerShown();
void autoplayMediaInlineAsync(const FullMsgId &msgId);
void showPeerProfile(const PeerId &peer);
inline void showPeerProfile(const PeerData *peer) {
showPeerProfile(peer->id);

View file

@ -231,11 +231,11 @@ InnerWidget::InnerWidget(
refreshItem(view);
}
}, lifetime());
Auth().data().itemPlayInlineRequest(
Auth().data().animationPlayInlineRequest(
) | rpl::start_with_next([this](auto item) {
if (const auto view = viewForItem(item)) {
if (const auto media = view->media()) {
media->playInline(true);
media->playAnimation();
}
}
}, lifetime());
@ -474,6 +474,20 @@ std::unique_ptr<HistoryView::Element> InnerWidget::elementCreate(
return std::make_unique<HistoryView::Service>(this, message);
}
void InnerWidget::elementAnimationAutoplayAsync(
not_null<const HistoryView::Element*> view) {
crl::on_main(this, [this, msgId = view->data()->fullId()] {
if (const auto item = App::histItemById(msgId)) {
if (const auto view = viewForItem(item)) {
if (const auto media = view->media()) {
media->autoplayAnimation();
}
}
}
});
}
void InnerWidget::saveState(not_null<SectionMemento*> memento) {
memento->setFilter(std::move(_filter));
memento->setAdmins(std::move(_admins));

View file

@ -74,6 +74,8 @@ public:
not_null<HistoryMessage*> message) override;
std::unique_ptr<HistoryView::Element> elementCreate(
not_null<HistoryService*> message) override;
void elementAnimationAutoplayAsync(
not_null<const HistoryView::Element*> element) override;
~InnerWidget();

View file

@ -164,20 +164,6 @@ rpl::producer<Data::MessagesSlice> Widget::listSource(
limitAfter);
}
HistoryView::Context Widget::elementContext() {
return HistoryView::Context::Feed;
}
std::unique_ptr<HistoryView::Element> Widget::elementCreate(
not_null<HistoryMessage*> message) {
return std::make_unique<HistoryView::Message>(this, message);
}
std::unique_ptr<HistoryView::Element> Widget::elementCreate(
not_null<HistoryService*> message) {
return std::make_unique<HistoryView::Service>(this, message);
}
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
auto result = std::make_unique<Memento>(_feed);
saveState(result.get());

View file

@ -67,13 +67,6 @@ public:
int limitBefore,
int limitAfter) override;
// HistoryView::ElementDelegate interface.
HistoryView::Context elementContext() override;
std::unique_ptr<HistoryView::Element> elementCreate(
not_null<HistoryMessage*> message) override;
std::unique_ptr<HistoryView::Element> elementCreate(
not_null<HistoryService*> message) override;
protected:
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;

View file

@ -2922,6 +2922,18 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
not_null<HistoryService*> message) override {
return std::make_unique<HistoryView::Service>(this, message);
}
void elementAnimationAutoplayAsync(
not_null<const HistoryView::Element*> view) override {
crl::on_main(&Auth(), [msgId = view->data()->fullId()] {
if (const auto item = App::histItemById(msgId)) {
if (const auto view = item->mainView()) {
if (const auto media = view->media()) {
media->autoplayAnimation();
}
}
}
});
}
};

View file

@ -639,33 +639,6 @@ bool HistoryItem::isEmpty() const {
&& !Has<HistoryMessageLogEntryOriginal>();
}
void HistoryItem::audioTrackUpdated() {
auto media = this->media();
if (!media) {
return;
}
// #TODO GIFs
//auto reader = media->getClipReader();
//if (!reader || reader->mode() != Media::Clip::Reader::Mode::Video) {
// return;
//}
//auto audio = reader->audioMsgId();
//auto current = Media::Player::mixer()->currentState(audio.type());
//if (current.id != audio || Media::Player::IsStoppedOrStopping(current.state)) {
// media->stopInline();
//} else if (Media::Player::IsPaused(current.state) || current.state == Media::Player::State::Pausing) {
// if (!reader->videoPaused()) {
// reader->pauseResumeVideo();
// }
//} else {
// if (reader->videoPaused()) {
// reader->pauseResumeVideo();
// }
//}
}
HistoryItem *HistoryItem::previousItem() const {
if (const auto view = mainView()) {
if (const auto previous = view->previousInBlocks()) {

View file

@ -266,8 +266,6 @@ public:
MessageGroupId groupId() const;
void audioTrackUpdated();
HistoryItem *previousItem() const;
HistoryItem *nextItem() const;

View file

@ -130,20 +130,14 @@ public:
virtual DocumentData *getDocument() const {
return nullptr;
}
virtual Media::Clip::Reader *getClipReader() {
return nullptr;
}
bool playInline(/*bool autoplay = false*/) {
return playInline(false);
void playAnimation() {
playAnimation(false);
}
virtual bool playInline(bool autoplay) {
return false;
void autoplayAnimation() {
playAnimation(true);
}
virtual void stopInline() {
}
virtual bool isRoundVideoPlaying() const {
return false;
virtual void stopAnimation() {
}
virtual QSize sizeForGrouping() const {
@ -243,6 +237,9 @@ protected:
QSize countCurrentSize(int newWidth) override;
Text createCaption(not_null<HistoryItem*> item) const;
virtual void playAnimation(bool autoplay) {
}
not_null<Element*> _parent;
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/media_audio.h"
#include "media/media_clip_reader.h"
#include "media/player/media_player_instance.h"
#include "media/player/media_player_round_controller.h"
#include "media/view/media_clip_playback.h"
#include "boxes/confirm_box.h"
#include "boxes/add_contact_box.h"
@ -1918,17 +1919,6 @@ void HistoryDocument::refreshParentId(not_null<HistoryItem*> realParent) {
}
}
bool HistoryDocument::playInline(bool autoplay) {
if (_data->isVoiceMessage()) {
DocumentOpenClickHandler::doOpen(_data, _parent->data(), ActionOnLoadPlayInline);
return true;
} else if (_data->isAudioFile()) {
Media::Player::instance()->play(AudioMsgId(_data, _parent->data()->fullId()));
return true;
}
return false;
}
TextWithEntities HistoryDocument::getCaption() const {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->_caption.originalTextWithEntities();
@ -1979,9 +1969,10 @@ QSize HistoryGif::countOptimalSize() {
setClipReader(Media::Clip::ReaderPointer::Bad());
}
if (_gif && _gif->ready()) {
tw = convertScale(_gif->width());
th = convertScale(_gif->height());
const auto reader = currentReader();
if (reader) {
tw = convertScale(reader->width());
th = convertScale(reader->height());
} else {
tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
if (!tw || !th) {
@ -2005,7 +1996,7 @@ QSize HistoryGif::countOptimalSize() {
auto maxWidth = qMax(tw, st::minPhotoSize);
auto minHeight = qMax(th, st::minPhotoSize);
accumulate_max(maxWidth, _parent->infoWidth() + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
if (!_gif || !_gif->ready()) {
if (!reader) {
accumulate_max(maxWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
}
if (_parent->hasBubble()) {
@ -2033,9 +2024,10 @@ QSize HistoryGif::countCurrentSize(int newWidth) {
auto availableWidth = newWidth;
int tw = 0, th = 0;
if (_gif && _gif->ready()) {
tw = convertScale(_gif->width());
th = convertScale(_gif->height());
const auto reader = currentReader();
if (reader) {
tw = convertScale(reader->width());
th = convertScale(reader->height());
} else {
tw = convertScale(_data->dimensions.width()), th = convertScale(_data->dimensions.height());
if (!tw || !th) {
@ -2065,14 +2057,31 @@ QSize HistoryGif::countCurrentSize(int newWidth) {
newWidth = qMax(tw, st::minPhotoSize);
auto newHeight = qMax(th, st::minPhotoSize);
accumulate_max(newWidth, _parent->infoWidth() + 2 * st::msgDateImgDelta + st::msgDateImgPadding.x());
if (_gif && _gif->ready()) {
if (!_gif->started()) {
if (reader) {
const auto own = (reader->mode() == Media::Clip::Reader::Mode::Gif);
if (own && !reader->started()) {
auto isRound = _data->isVideoMessage();
auto inWebPage = (_parent->media() != this);
auto roundRadius = isRound ? ImageRoundRadius::Ellipse : inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
auto roundCorners = (isRound || inWebPage) ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
| ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
_gif->start(_thumbw, _thumbh, newWidth, newHeight, roundRadius, roundCorners);
auto roundRadius = isRound
? ImageRoundRadius::Ellipse
: inWebPage
? ImageRoundRadius::Small
: ImageRoundRadius::Large;
auto roundCorners = (isRound || inWebPage)
? RectPart::AllCorners
: ((isBubbleTop()
? (RectPart::TopLeft | RectPart::TopRight)
: RectPart::None)
| ((isBubbleBottom() && _caption.isEmpty())
? (RectPart::BottomLeft | RectPart::BottomRight)
: RectPart::None));
reader->start(
_thumbw,
_thumbh,
newWidth,
newHeight,
roundRadius,
roundCorners);
}
} else {
accumulate_max(newWidth, gifMaxStatusWidth(_data) + 2 * (st::msgDateImgDelta + st::msgDateImgPadding.x()));
@ -2117,11 +2126,12 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
auto displayLoading = (item->id < 0) || _data->displayLoading();
auto selected = (selection == FullSelection);
auto videoFinished = _gif
&& (_gif->mode() == Media::Clip::Reader::Mode::Video)
&& (_gif->state() == Media::Clip::State::Finished);
if (loaded && cAutoPlayGif() && ((!_gif && !_gif.isBad()) || videoFinished)) {
Ui::autoplayMediaInlineAsync(item->fullId());
if (loaded
&& cAutoPlayGif()
&& !_gif
&& !_gif.isBad()
&& !activeRoundVideo()) {
_parent->delegate()->elementAnimationAutoplayAsync(_parent);
}
auto paintx = 0, painty = 0, paintw = width(), painth = height();
@ -2133,7 +2143,11 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
auto isRound = _data->isVideoMessage();
auto displayMute = false;
auto animating = (_gif && _gif->started());
const auto reader = currentReader();
const auto playingVideo = reader
? (reader->mode() == Media::Clip::Reader::Mode::Video)
: false;
const auto animating = reader && reader->started();
if (!animating || item->id < 0) {
if (displayLoading) {
@ -2143,7 +2157,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
}
}
updateStatusText();
} else if (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video) {
} else if (playingVideo) {
updateStatusText();
}
auto radial = isRadialAnimation(ms);
@ -2180,18 +2194,16 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
if (animating) {
auto paused = App::wnd()->controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Any);
if (isRound) {
if (_gif->mode() == Media::Clip::Reader::Mode::Video) {
if (playingVideo) {
paused = false;
} else {
displayMute = true;
}
}
p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, usew, painth, roundRadius, roundCorners, paused ? 0 : ms));
p.drawPixmap(rthumb.topLeft(), reader->current(_thumbw, _thumbh, usew, painth, roundRadius, roundCorners, paused ? 0 : ms));
if (displayMute) {
_roundPlayback.reset();
} else if (_roundPlayback) {
auto value = _roundPlayback->value(ms);
if (const auto playback = videoPlayback()) {
const auto value = playback->value(ms);
if (value > 0.) {
auto pen = st::historyVideoMessageProgressFg->p;
auto was = p.pen();
@ -2220,7 +2232,7 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners);
}
if (radial || _gif.isBad() || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif()))) {
if (radial || (!reader && (_gif.isBad() || (!loaded && !_data->loading()) || !cAutoPlayGif()))) {
auto radialOpacity = (radial && loaded && item->id > 0) ? _animation->radial.opacity() : 1.;
auto inner = QRect(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
p.setPen(Qt::NoPen);
@ -2594,12 +2606,12 @@ void HistoryGif::updateStatusText() const {
statusSize = _data->loadOffset();
} else if (_data->loaded()) {
statusSize = FileStatusSizeLoaded;
if (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video) {
if (const auto video = activeRoundPlayer()) {
statusSize = -1 - _data->duration();
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice);
if (state.id == _gif->audioMsgId()) {
if (state.length) {
const auto state = Media::Player::mixer()->currentState(
AudioMsgId::Type::Voice);
if (state.id == video->audioMsgId() && state.length) {
auto position = int64(0);
if (Media::Player::IsStoppedAtEnd(state.state)) {
position = state.length;
@ -2608,10 +2620,6 @@ void HistoryGif::updateStatusText() const {
}
accumulate_max(statusSize, -1 - int((state.length - position) / state.frequency + 1));
}
if (_roundPlayback) {
_roundPlayback->updateState(state);
}
}
}
} else {
statusSize = FileStatusSizeReady;
@ -2646,63 +2654,96 @@ int HistoryGif::additionalWidth(const HistoryMessageVia *via, const HistoryMessa
return result;
}
bool HistoryGif::playInline(bool autoplay) {
Media::Player::RoundController *HistoryGif::activeRoundVideo() const {
return App::wnd()->controller()->roundVideo(_parent->data());
}
Media::Clip::Reader *HistoryGif::activeRoundPlayer() const {
if (const auto video = activeRoundVideo()) {
if (const auto result = video->reader()) {
if (result->ready()) {
return result;
}
}
}
return nullptr;
}
Media::Clip::Reader *HistoryGif::currentReader() const {
if (const auto result = activeRoundPlayer()) {
return result;
}
return (_gif && _gif->ready()) ? _gif.get() : nullptr;
}
Media::Clip::Playback *HistoryGif::videoPlayback() const {
if (const auto video = activeRoundVideo()) {
return video->playback();
}
return nullptr;
}
void HistoryGif::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
const auto reader = _gif.get();
if (!reader) {
return;
}
switch (notification) {
case NotificationReinit: {
auto stopped = false;
if (reader->autoPausedGif()) {
auto amVisible = false;
Auth().data().queryItemVisibility().notify(
{ _parent->data(), &amVisible },
true);
if (!amVisible) { // Stop animation if it is not visible.
stopAnimation();
stopped = true;
}
}
if (!stopped) {
Auth().data().requestViewResize(_parent);
}
} break;
case NotificationRepaint: {
if (!reader->currentDisplayed()) {
Auth().data().requestViewRepaint(_parent);
}
} break;
}
}
void HistoryGif::playAnimation(bool autoplay) {
if (_data->isVideoMessage() && !autoplay) {
return;
}
using Mode = Media::Clip::Reader::Mode;
if (_data->isVideoMessage() && _gif) {
// Stop autoplayed silent video when we start playback by click.
// Stop finished video message when autoplay starts.
if (!autoplay) {
if (_gif->mode() == Mode::Gif) {
stopInline();
} else {
_gif->pauseResumeVideo();
return true;
}
} else if (autoplay && _gif->mode() == Mode::Video && _gif->state() == Media::Clip::State::Finished) {
stopInline();
}
}
if (_gif) {
stopInline();
stopAnimation();
} else if (_data->loaded(DocumentData::FilePathResolveChecked)) {
if (!cAutoPlayGif()) {
Auth().data().stopAutoplayAnimations();
}
const auto mode = (!autoplay && _data->isVideoMessage())
? Mode::Video
: Mode::Gif;
setClipReader(Media::Clip::MakeReader(_data, _parent->data()->fullId(), [this](Media::Clip::Notification notification) {
_parent->clipCallback(notification);
}, mode));
if (mode == Mode::Video) {
_roundPlayback = std::make_unique<Media::Clip::Playback>();
_roundPlayback->setValueChangedCallback([this](float64 value) {
Auth().data().requestViewRepaint(_parent);
});
if (App::main()) {
Auth().data().markMediaRead(_data);
}
App::wnd()->controller()->enableGifPauseReason(Window::GifPauseReason::RoundPlaying);
}
setClipReader(Media::Clip::MakeReader(
_data,
_parent->data()->fullId(),
[=](auto notification) { clipCallback(notification); },
Mode::Gif));
if (_gif && autoplay) {
_gif->setAutoplay();
}
}
return true;
}
bool HistoryGif::isRoundVideoPlaying() const {
return (_gif && _gif->mode() == Media::Clip::Reader::Mode::Video);
}
void HistoryGif::stopInline() {
if (isRoundVideoPlaying()) {
App::wnd()->controller()->disableGifPauseReason(Window::GifPauseReason::RoundPlaying);
}
void HistoryGif::stopAnimation() {
if (_gif) {
clearClipReader();
Auth().data().requestViewResize(_parent);
Auth().data().notifyViewLayoutChange(_parent);
_data->forget();
}
}
void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) {
@ -3805,6 +3846,17 @@ void HistoryWebPage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool p
_attach->clickHandlerPressedChanged(p, pressed);
}
}
void HistoryWebPage::playAnimation(bool autoplay) {
if (_attach) {
if (autoplay) {
_attach->autoplayAnimation();
} else {
_attach->playAnimation();
}
}
}
bool HistoryWebPage::isDisplayed() const {
const auto item = _parent->data();
return !_data->pendingTill
@ -4228,6 +4280,16 @@ ImagePtr HistoryGame::replyPreview() {
return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
}
void HistoryGame::playAnimation(bool autoplay) {
if (_attach) {
if (autoplay) {
_attach->autoplayAnimation();
} else {
_attach->playAnimation();
}
}
}
QMargins HistoryGame::inBubblePadding() const {
auto lshift = st::msgPadding.left() + st::webPageLeft;
auto rshift = st::msgPadding.right();

View file

@ -31,6 +31,10 @@ namespace Media {
namespace Clip {
class Playback;
} // namespace Clip
namespace Player {
class RoundController;
} // namespace Player
} // namespace Media
namespace Ui {
@ -364,8 +368,6 @@ public:
return _data;
}
bool playInline(bool autoplay) override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
@ -447,13 +449,8 @@ public:
DocumentData *getDocument() const override {
return _data;
}
Media::Clip::Reader *getClipReader() override {
return _gif.get();
}
bool playInline(bool autoplay) override;
void stopInline() override;
bool isRoundVideoPlaying() const override;
void stopAnimation() override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
@ -489,8 +486,14 @@ protected:
}
private:
void playAnimation(bool autoplay) override;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;
Media::Player::RoundController *activeRoundVideo() const;
Media::Clip::Reader *activeRoundPlayer() const;
Media::Clip::Reader *currentReader() const;
Media::Clip::Playback *videoPlayback() const;
void clipCallback(Media::Clip::Notification notification);
bool needInfoDisplay() const;
int additionalWidth(
@ -506,8 +509,6 @@ private:
int _thumbw = 1;
int _thumbh = 1;
Text _caption;
mutable std::unique_ptr<Media::Clip::Playback> _roundPlayback;
Media::Clip::ReaderPointer _gif;
void setStatusSize(int newSize) const;
@ -732,14 +733,8 @@ public:
DocumentData *getDocument() const override {
return _attach ? _attach->getDocument() : nullptr;
}
Media::Clip::Reader *getClipReader() override {
return _attach ? _attach->getClipReader() : nullptr;
}
bool playInline(bool autoplay) override {
return _attach ? _attach->playInline(autoplay) : false;
}
void stopInline() override {
if (_attach) _attach->stopInline();
void stopAnimation() override {
if (_attach) _attach->stopAnimation();
}
bool hasReplyPreview() const override;
@ -766,6 +761,7 @@ public:
~HistoryWebPage();
private:
void playAnimation(bool autoplay) override;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;
@ -842,14 +838,8 @@ public:
DocumentData *getDocument() const override {
return _attach ? _attach->getDocument() : nullptr;
}
Media::Clip::Reader *getClipReader() override {
return _attach ? _attach->getClipReader() : nullptr;
}
bool playInline(bool autoplay) override {
return _attach ? _attach->playInline(autoplay) : false;
}
void stopInline() override {
if (_attach) _attach->stopInline();
void stopAnimation() override {
if (_attach) _attach->stopAnimation();
}
bool hasReplyPreview() const override {
@ -878,6 +868,7 @@ public:
~HistoryGame();
private:
void playAnimation(bool autoplay) override;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;

View file

@ -573,11 +573,11 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null<Window::Controller*> cont
) | rpl::start_with_next([this](auto item) {
item->refreshMainView();
}, lifetime());
Auth().data().itemPlayInlineRequest(
Auth().data().animationPlayInlineRequest(
) | rpl::start_with_next([this](auto item) {
if (const auto view = item->mainView()) {
if (const auto media = view->media()) {
media->playInline(true);
media->playAnimation();
}
}
}, lifetime());

View file

@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_element.h"
#include "media/media_clip_reader.h"
#include "history/history_item_components.h"
#include "history/history_item.h"
#include "history/history_media.h"
@ -191,51 +190,6 @@ void Element::nextInBlocksRemoved() {
setAttachToNext(false);
}
void Element::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
const auto media = this->media();
if (!media) {
return;
}
const auto reader = media->getClipReader();
if (!reader) {
return;
}
switch (notification) {
case NotificationReinit: {
auto stopped = false;
if (reader->autoPausedGif()) {
auto amVisible = false;
Auth().data().queryItemVisibility().notify({ data(), &amVisible }, true);
if (!amVisible) { // stop animation if it is not visible
media->stopInline();
if (const auto document = media->getDocument()) {
document->forget();
}
stopped = true;
}
} else if (reader->mode() == Reader::Mode::Video
&& reader->state() == State::Finished) {
// Stop finished video message.
media->stopInline();
}
if (!stopped) {
Auth().data().requestViewResize(this);
Auth().data().notifyViewLayoutChange(this);
}
} break;
case NotificationRepaint: {
if (!reader->currentDisplayed()) {
Auth().data().requestViewRepaint(this);
}
} break;
}
}
void Element::refreshDataId() {
if (const auto media = this->media()) {
media->refreshParentId(data());

View file

@ -37,6 +37,8 @@ public:
not_null<HistoryMessage*> message) = 0;
virtual std::unique_ptr<Element> elementCreate(
not_null<HistoryService*> message) = 0;
virtual void elementAnimationAutoplayAsync(
not_null<const Element*> element) = 0;
};
@ -180,8 +182,6 @@ public:
void previousInBlocksChanged();
void nextInBlocksRemoved();
void clipCallback(Media::Clip::Notification notification);
virtual ~Element();
protected:

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_message.h"
#include "history/history_item_components.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_message.h"
#include "history/view/history_view_service_message.h"
#include "chat_helpers/message_field.h"
#include "mainwindow.h"
@ -223,13 +224,13 @@ ListWidget::ListWidget(
_scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); });
Auth().data().viewRepaintRequest(
) | rpl::start_with_next([this](auto view) {
if (view->delegate() == _delegate) {
if (view->delegate() == this) {
repaintItem(view);
}
}, lifetime());
Auth().data().viewResizeRequest(
) | rpl::start_with_next([this](auto view) {
if (view->delegate() == _delegate) {
if (view->delegate() == this) {
updateSize();
}
}, lifetime());
@ -239,11 +240,11 @@ ListWidget::ListWidget(
refreshItem(view);
}
}, lifetime());
Auth().data().itemPlayInlineRequest(
Auth().data().animationPlayInlineRequest(
) | rpl::start_with_next([this](auto item) {
if (const auto view = viewForItem(item)) {
if (const auto media = view->media()) {
media->playInline(true);
media->playAnimation();
}
}
}, lifetime());
@ -329,7 +330,7 @@ not_null<Element*> ListWidget::enforceViewForItem(
}
const auto [i, ok] = _views.emplace(
item,
item->createView(_delegate));
item->createView(this));
return i->second.get();
}
@ -511,6 +512,33 @@ QPoint ListWidget::tooltipPos() const {
return _mousePosition;
}
Context ListWidget::elementContext() {
return _delegate->listContext();
}
std::unique_ptr<Element> ListWidget::elementCreate(
not_null<HistoryMessage*> message) {
return std::make_unique<Message>(this, message);
}
std::unique_ptr<Element> ListWidget::elementCreate(
not_null<HistoryService*> message) {
return std::make_unique<Service>(this, message);
}
void ListWidget::elementAnimationAutoplayAsync(
not_null<const Element*> view) {
crl::on_main(this, [this, msgId = view->data()->fullId()]{
if (const auto item = App::histItemById(msgId)) {
if (const auto view = viewForItem(item)) {
if (const auto media = view->media()) {
media->autoplayAnimation();
}
}
}
});
}
void ListWidget::saveState(not_null<ListMemento*> memento) {
memento->setAroundPosition(_aroundPosition);
auto state = countScrollState();
@ -1432,7 +1460,7 @@ void ListWidget::refreshItem(not_null<const Element*> view) {
_views.erase(item);
const auto [i, ok] = _views.emplace(
item,
item->createView(_delegate));
item->createView(this));
const auto was = view;
const auto now = i->second.get();
_items[index] = now;

View file

@ -27,7 +27,7 @@ namespace HistoryView {
enum class Context : char;
class ListDelegate : public ElementDelegate {
class ListDelegate {
public:
virtual Context listContext() = 0;
virtual void listScrollTo(int top) = 0;
@ -77,6 +77,7 @@ private:
class ListWidget final
: public Ui::RpWidget
, public ElementDelegate
, public Ui::AbstractTooltipShower
, private base::Subscriber {
public:
@ -100,6 +101,15 @@ public:
QString tooltipText() const override;
QPoint tooltipPos() const override;
// ElementDelegate interface.
Context elementContext() override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryMessage*> message) override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryService*> message) override;
void elementAnimationAutoplayAsync(
not_null<const Element*> view) override;
~ListWidget();
protected:

View file

@ -171,15 +171,25 @@ StackItemSection::StackItemSection(
}
template <typename ToggleCallback, typename DraggedCallback>
MainWidget::Float::Float(QWidget *parent, HistoryItem *item, ToggleCallback toggle, DraggedCallback dragged)
MainWidget::Float::Float(
QWidget *parent,
not_null<Window::Controller*> controller,
not_null<HistoryItem*> item,
ToggleCallback toggle,
DraggedCallback dragged)
: animationSide(RectPart::Right)
, column(Window::Column::Second)
, corner(RectPart::TopRight)
, widget(parent, item, [this, toggle = std::move(toggle)](bool visible) {
, widget(
parent,
controller,
item,
[this, toggle = std::move(toggle)](bool visible) {
toggle(this, visible);
}, [this, dragged = std::move(dragged)](bool closed) {
},
[this, dragged = std::move(dragged)](bool closed) {
dragged(this, closed);
}) {
}) {
}
MainWidget::MainWidget(
@ -317,31 +327,43 @@ MainWidget::MainWidget(
}
void MainWidget::checkCurrentFloatPlayer() {
auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
auto fullId = state.contextId();
auto last = currentFloatPlayer();
if (!last || last->widget->detached() || last->widget->item()->fullId() != fullId) {
const auto state = Media::Player::instance()->current(AudioMsgId::Type::Voice);
const auto fullId = state.contextId();
const auto last = currentFloatPlayer();
if (last
&& !last->widget->detached()
&& last->widget->item()->fullId() == fullId) {
return;
}
if (last) {
last->widget->detach();
}
if (auto item = App::histItemById(fullId)) {
if (auto media = item->media()) {
if (auto document = media->document()) {
if (const auto item = App::histItemById(fullId)) {
if (const auto media = item->media()) {
if (const auto document = media->document()) {
if (document->isVideoMessage()) {
_playerFloats.push_back(std::make_unique<Float>(this, item, [this](not_null<Float*> instance, bool visible) {
createFloatPlayer(item);
}
}
}
}
}
void MainWidget::createFloatPlayer(not_null<HistoryItem*> item) {
_playerFloats.push_back(std::make_unique<Float>(
this,
_controller,
item,
[this](not_null<Float*> instance, bool visible) {
instance->hiddenByWidget = !visible;
toggleFloatPlayer(instance);
}, [this](not_null<Float*> instance, bool closed) {
},
[this](not_null<Float*> instance, bool closed) {
finishFloatPlayerDrag(instance, closed);
}));
currentFloatPlayer()->column = Auth().settings().floatPlayerColumn();
currentFloatPlayer()->corner = Auth().settings().floatPlayerCorner();
checkFloatPlayerVisibility();
}
}
}
}
}
}
void MainWidget::toggleFloatPlayer(not_null<Float*> instance) {
@ -1579,7 +1601,7 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
}
}
if (state.id == audioId && (audioId.type() == AudioMsgId::Type::Song || audioId.type() == AudioMsgId::Type::Voice)) {
if (state.id == audioId) {
if (!Media::Player::IsStoppedOrStopping(state.state)) {
createPlayer();
}
@ -1587,7 +1609,6 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
if (const auto item = App::histItemById(audioId.contextId())) {
Auth().data().requestItemRepaint(item);
item->audioTrackUpdated();
}
if (const auto items = InlineBots::Layout::documentItems()) {
if (const auto i = items->find(document); i != items->end()) {

View file

@ -380,7 +380,12 @@ protected:
private:
struct Float {
template <typename ToggleCallback, typename DraggedCallback>
Float(QWidget *parent, HistoryItem *item, ToggleCallback callback, DraggedCallback dragged);
Float(
QWidget *parent,
not_null<Window::Controller*> controller,
not_null<HistoryItem*> item,
ToggleCallback toggle,
DraggedCallback dragged);
bool hiddenByWidget = false;
bool hiddenByHistory = false;
@ -506,6 +511,7 @@ private:
void clearCachedBackground();
void checkCurrentFloatPlayer();
void createFloatPlayer(not_null<HistoryItem*> item);
void toggleFloatPlayer(not_null<Float*> instance);
void checkFloatPlayerVisibility();
void updateFloatPlayerPosition(not_null<Float*> instance);

View file

@ -15,8 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h"
#include "history/view/history_view_element.h"
#include "media/media_clip_reader.h"
#include "media/view/media_clip_playback.h"
#include "media/media_audio.h"
#include "media/view/media_clip_playback.h"
#include "media/player/media_player_round_controller.h"
#include "window/window_controller.h"
#include "auth_session.h"
#include "styles/style_media_player.h"
#include "styles/style_history.h"
@ -26,10 +28,12 @@ namespace Player {
Float::Float(
QWidget *parent,
HistoryItem *item,
not_null<Window::Controller*> controller,
not_null<HistoryItem*> item,
base::lambda<void(bool visible)> toggleCallback,
base::lambda<void(bool closed)> draggedCallback)
: RpWidget(parent)
, _controller(controller)
, _item(item)
, _toggleCallback(std::move(toggleCallback))
, _draggedCallback(std::move(draggedCallback)) {
@ -47,7 +51,7 @@ Float::Float(
prepareShadow();
rpl::merge(
//Auth().data().viewLayoutChanged(
//Auth().data().viewLayoutChanged( // #TODO not needed?
//) | rpl::map(
// [](auto view) { return view->data(); }
//),
@ -103,12 +107,10 @@ float64 Float::outRatio() const {
}
void Float::mouseReleaseEvent(QMouseEvent *e) {
if (_down) {
_down = false;
// #TODO float player
//if (auto media = _item ? _item->getMedia() : nullptr) {
// media->playInline();
//}
if (base::take(_down) && _item) {
if (const auto controller = _controller->roundVideo(_item)) {
controller->pauseResume();
}
}
if (_drag) {
finishDrag(outRatio() < 0.5);
@ -125,10 +127,9 @@ void Float::finishDrag(bool closed) {
void Float::mouseDoubleClickEvent(QMouseEvent *e) {
if (_item) {
// Handle second click.
// #TODO float player
//if (auto media = _item->getMedia()) {
// media->playInline();
//}
if (const auto controller = _controller->roundVideo(_item)) {
controller->pauseResume();
}
Ui::showPeerHistoryAtItem(_item);
}
}
@ -175,7 +176,8 @@ void Float::paintEvent(QPaintEvent *e) {
auto inner = getInnerRect();
p.drawImage(inner.topLeft(), _frame);
auto progress = _roundPlayback ? _roundPlayback->value(getms()) : 1.;
const auto playback = getPlayback();
const auto progress = playback ? playback->value(getms()) : 1.;
if (progress > 0.) {
auto pen = st::historyVideoMessageProgressFg->p;
auto was = p.pen();
@ -198,19 +200,31 @@ void Float::paintEvent(QPaintEvent *e) {
}
Clip::Reader *Float::getReader() const {
// #TODO float player
//if (auto media = _item ? _item->getMedia() : nullptr) {
// if (auto reader = media->getClipReader()) {
// if (reader->started() && reader->mode() == Clip::Reader::Mode::Video) {
// return reader;
// }
// }
//}
if (detached()) {
return nullptr;
}
if (const auto controller = _controller->roundVideo(_item)) {
if (const auto reader = controller->reader()) {
if (reader->started()) {
return reader;
}
}
}
return nullptr;
}
Clip::Playback *Float::getPlayback() const {
if (detached()) {
return nullptr;
}
if (const auto controller = _controller->roundVideo(_item)) {
return controller->playback();
}
return nullptr;
}
bool Float::hasFrame() const {
if (auto reader = getReader()) {
if (const auto reader = getReader()) {
return !reader->current().isNull();
}
return false;
@ -219,14 +233,15 @@ bool Float::hasFrame() const {
bool Float::fillFrame() {
auto creating = _frame.isNull();
if (creating) {
_frame = QImage(getInnerRect().size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
_frame = QImage(
getInnerRect().size() * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
_frame.setDevicePixelRatio(cRetinaFactor());
}
auto frameInner = [this] {
return QRect(0, 0, _frame.width() / cIntRetinaFactor(), _frame.height() / cIntRetinaFactor());
auto frameInner = [&] {
return QRect(QPoint(), _frame.size() / cIntRetinaFactor());
};
if (auto reader = getReader()) {
updatePlayback();
if (const auto reader = getReader()) {
auto frame = reader->current();
if (!frame.isNull()) {
_frame.fill(Qt::transparent);
@ -249,21 +264,6 @@ bool Float::fillFrame() {
return false;
}
void Float::updatePlayback() {
if (_item) {
if (!_roundPlayback) {
_roundPlayback = std::make_unique<Media::Clip::Playback>();
_roundPlayback->setValueChangedCallback([this](float64 value) {
update();
});
}
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Voice);
if (state.id.contextId() == _item->fullId()) {
_roundPlayback->updateState(state);
}
}
}
void Float::repaintItem() {
update();
if (hasFrame() && _toggleCallback) {

View file

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
namespace Window {
class Controller;
} // namespace Window
namespace Media {
namespace Clip {
class Playback;
@ -18,7 +22,12 @@ namespace Player {
class Float : public Ui::RpWidget, private base::Subscriber {
public:
Float(QWidget *parent, HistoryItem *item, base::lambda<void(bool visible)> toggleCallback, base::lambda<void(bool closed)> draggedCallback);
Float(
QWidget *parent,
not_null<Window::Controller*> controller,
not_null<HistoryItem*> item,
base::lambda<void(bool visible)> toggleCallback,
base::lambda<void(bool closed)> draggedCallback);
HistoryItem *item() const {
return _item;
@ -59,14 +68,15 @@ protected:
private:
float64 outRatio() const;
Clip::Reader *getReader() const;
Clip::Playback *getPlayback() const;
void repaintItem();
void prepareShadow();
bool hasFrame() const;
bool fillFrame();
QRect getInnerRect() const;
void updatePlayback();
void finishDrag(bool closed);
not_null<Window::Controller*> _controller;
HistoryItem *_item = nullptr;
base::lambda<void(bool visible)> _toggleCallback;
@ -81,8 +91,6 @@ private:
QPoint _dragLocalPoint;
base::lambda<void(bool closed)> _draggedCallback;
std::unique_ptr<Clip::Playback> _roundPlayback;
};
} // namespace Player

View file

@ -10,12 +10,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "media/media_audio.h"
#include "media/media_audio_capture.h"
#include "messenger.h"
#include "auth_session.h"
#include "calls/calls_instance.h"
#include "history/history.h"
#include "history/history_item.h"
#include "data/data_media_types.h"
#include "window/window_controller.h"
#include "messenger.h"
#include "mainwindow.h"
#include "auth_session.h"
namespace Media {
namespace Player {
@ -230,13 +232,15 @@ bool Instance::moveInPlaylist(
item->fullId()
});
}
if (document->isAudioFile()) {
if (document->isAudioFile()
|| document->isVoiceMessage()
|| document->isVideoMessage()) {
play(AudioMsgId(document, item->fullId()));
} else {
DocumentOpenClickHandler::doOpen(
document,
item,
ActionOnLoadPlayInline);
//DocumentOpenClickHandler::doOpen(
// document,
// item,
// ActionOnLoadPlayInline);
}
return true;
}
@ -300,12 +304,9 @@ void Instance::play(const AudioMsgId &audioId) {
documentLoadProgress(document);
}
} else if (document->isVideoMessage()) {
// #TODO float player
//if (const auto item = App::histItemById(audioId.contextId())) {
// if (const auto media = item->getMedia()) {
// media->playInline();
// }
//}
if (const auto item = App::histItemById(audioId.contextId())) {
App::wnd()->controller()->startRoundVideo(item);
}
}
}

View file

@ -0,0 +1,179 @@
/*
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/player/media_player_round_controller.h"
#include "media/media_clip_reader.h"
#include "media/media_audio.h"
#include "media/player/media_player_instance.h"
#include "media/view/media_clip_playback.h"
#include "history/history_item.h"
#include "window/window_controller.h"
#include "data/data_media_types.h"
#include "data/data_document.h"
#include "data/data_session.h"
#include "auth_session.h"
namespace Media {
namespace Player {
struct RoundController::CreateTag {
};
std::unique_ptr<RoundController> RoundController::TryStart(
not_null<Window::Controller*> parent,
not_null<HistoryItem*> item) {
const auto media = item->media();
if (!media) {
return nullptr;
}
const auto document = media->document();
if (!document || !document->isVideoMessage()) {
return nullptr;
}
return std::make_unique<RoundController>(CreateTag(), parent, item);
}
RoundController::RoundController(
CreateTag&&,
not_null<Window::Controller*> parent,
not_null<HistoryItem*> item)
: _parent(parent)
, _data(item->media()->document())
, _context(item) {
Expects(_data->isVideoMessage());
subscribe(instance()->updatedNotifier(), [this](const TrackState &state) {
handleAudioUpdate(state);
});
_reader = Clip::MakeReader(
_data,
_context->fullId(),
[=](Clip::Notification notification) { callback(notification); },
Clip::Reader::Mode::Video);
_playback = std::make_unique<Clip::Playback>();
_playback->setValueChangedCallback([=](float64 value) {
Auth().data().requestItemRepaint(_context);
});
Auth().data().markMediaRead(_data);
Auth().data().itemRemoved(
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
if (item == _context) {
stop(State::Stopped);
}
}, lifetime());
Auth().data().itemRepaintRequest(
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
if (item == _context) {
checkReaderState();
}
}, lifetime());
}
rpl::lifetime &RoundController::lifetime() {
return _lifetime;
}
FullMsgId RoundController::contextId() const {
return _context->fullId();
}
void RoundController::pauseResume() {
if (checkReaderState()) {
_reader->pauseResumeVideo();
}
}
Clip::Reader *RoundController::reader() const {
return _reader ? _reader.get() : nullptr;
}
Clip::Playback *RoundController::playback() const {
return _playback.get();
}
void RoundController::handleAudioUpdate(const TrackState &state) {
if (state.id.type() != AudioMsgId::Type::Voice) {
return;
}
const auto audio = _reader->audioMsgId();
const auto another = (state.id != _reader->audioMsgId());
const auto stopped = IsStoppedOrStopping(state.state);
if ((another && !stopped) || (!another && stopped)) {
stop(State::Stopped);
return;
} else if (another) {
return;
}
if (_playback) {
_playback->updateState(state);
}
if (IsPaused(state.state) || state.state == State::Pausing) {
if (!_reader->videoPaused()) {
_reader->pauseResumeVideo();
}
} else {
if (_reader->videoPaused()) {
_reader->pauseResumeVideo();
}
}
}
void RoundController::callback(Clip::Notification notification) {
if (!_reader) {
return;
}
switch (notification) {
case Clip::NotificationReinit: {
if (checkReaderState()) {
Auth().data().requestItemResize(_context);
}
} break;
case Clip::NotificationRepaint: {
Auth().data().requestItemRepaint(_context);
} break;
}
}
bool RoundController::checkReaderState() {
if (!_reader) {
return false;
}
const auto state = _reader->state();
if (state == Media::Clip::State::Error) {
stop(State::StoppedAtError);
return false;
} else if (state == Media::Clip::State::Finished) {
stop(State::StoppedAtEnd);
return false;
} else if (_reader->ready() && !_reader->started()) {
const auto size = QSize(_reader->width(), _reader->height())
/ cIntRetinaFactor();
_reader->start(
size.width(),
size.height(),
size.width(),
size.height(),
ImageRoundRadius::Ellipse,
RectPart::AllCorners);
}
return true;
}
void RoundController::stop(State state) {
if (const auto audioId = _reader->audioMsgId()) {
mixer()->stop(audioId, state);
}
_parent->roundVideoFinished(this);
}
RoundController::~RoundController() = default;
} // namespace Player
} // namespace Media

View file

@ -0,0 +1,67 @@
/*
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
class HistoryItem;
class AudioMsgId;
namespace Window {
class Controller;
} // namespace Window
namespace Media {
namespace Clip {
class Playback;
} // namespace Clip
} // namespace Media
namespace Media {
namespace Player {
struct TrackState;
enum class State;
class RoundController : private base::Subscriber {
struct CreateTag;
public:
static std::unique_ptr<RoundController> TryStart(
not_null<Window::Controller*> parent,
not_null<HistoryItem*> item);
FullMsgId contextId() const;
void pauseResume();
Clip::Reader *reader() const;
Clip::Playback *playback() const;
rpl::lifetime &lifetime();
RoundController(
CreateTag&&,
not_null<Window::Controller*> parent,
not_null<HistoryItem*> item);
~RoundController();
private:
void stop(State state);
bool checkReaderState();
void callback(Clip::Notification notification);
void handleAudioUpdate(const TrackState &audioId);
not_null<Window::Controller*> _parent;
not_null<DocumentData*> _data;
not_null<HistoryItem*> _context;
Clip::ReaderPointer _reader;
std::unique_ptr<Clip::Playback> _playback;
rpl::lifetime _lifetime;
};
} // namespace Player
} // namespace Media

View file

@ -12,16 +12,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "history/view/history_view_element.h"
#include "media/player/media_player_round_controller.h"
#include "data/data_session.h"
#include "boxes/calendar_box.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "styles/style_window.h"
#include "styles/style_dialogs.h"
#include "boxes/calendar_box.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "styles/style_window.h"
#include "styles/style_dialogs.h"
namespace Window {
Controller::Controller(not_null<MainWindow*> window)
: _window(window) {
Auth().data().animationPlayInlineRequest(
) | rpl::start_with_next([this](auto item) {
if (const auto video = roundVideo(item)) {
video->pauseResume();
} else {
startRoundVideo(item);
}
}, lifetime());
}
void Controller::enableGifPauseReason(GifPauseReason reason) {
if (!(_gifPauseReasons & reason)) {
auto notify = (static_cast<int>(_gifPauseReasons) < static_cast<int>(reason));
@ -405,4 +419,40 @@ not_null<MainWidget*> Controller::chats() const {
return App::wnd()->chatsWidget();
}
bool Controller::startRoundVideo(not_null<HistoryItem*> context) {
if (auto video = RoundController::TryStart(this, context)) {
enableGifPauseReason(Window::GifPauseReason::RoundPlaying);
_roundVideo = std::move(video);
return true;
}
return false;
}
auto Controller::currentRoundVideo() const -> RoundController* {
return _roundVideo.get();
}
auto Controller::roundVideo(not_null<const HistoryItem*> context) const
-> RoundController* {
return roundVideo(context->fullId());
}
auto Controller::roundVideo(FullMsgId contextId) const -> RoundController* {
if (const auto result = currentRoundVideo()) {
if (result->contextId() == contextId) {
return result;
}
}
return nullptr;
}
void Controller::roundVideoFinished(not_null<RoundController*> video) {
if (video == _roundVideo.get()) {
_roundVideo = nullptr;
disableGifPauseReason(Window::GifPauseReason::RoundPlaying);
}
}
Controller::~Controller() = default;
} // namespace Window

View file

@ -14,6 +14,12 @@ class MainWidget;
class HistoryMessage;
class HistoryService;
namespace Media {
namespace Player {
class RoundController;
} // namespace Player
} // namespace Media
namespace Window {
class LayerWidget;
@ -89,12 +95,13 @@ public:
not_null<History*> history,
const SectionShow &params = SectionShow());
virtual ~Navigation() = default;
};
class Controller : public Navigation {
public:
Controller(not_null<MainWindow*> window) : _window(window) {
}
Controller(not_null<MainWindow*> window);
not_null<MainWindow*> window() const {
return _window;
@ -197,6 +204,19 @@ public:
return this;
}
using RoundController = Media::Player::RoundController;
bool startRoundVideo(not_null<HistoryItem*> context);
RoundController *currentRoundVideo() const;
RoundController *roundVideo(not_null<const HistoryItem*> context) const;
RoundController *roundVideo(FullMsgId contextId) const;
void roundVideoFinished(not_null<RoundController*> video);
rpl::lifetime &lifetime() {
return _lifetime;
}
~Controller();
private:
int minimalThreeColumnWidth() const;
not_null<MainWidget*> chats() const;
@ -220,6 +240,10 @@ private:
base::Variable<bool> _dialogsListFocused = { false };
base::Variable<bool> _dialogsListDisplayForced = { false };
std::unique_ptr<RoundController> _roundVideo;
rpl::lifetime _lifetime;
};
} // namespace Window

View file

@ -360,6 +360,8 @@
<(src_loc)/media/player/media_player_instance.h
<(src_loc)/media/player/media_player_panel.cpp
<(src_loc)/media/player/media_player_panel.h
<(src_loc)/media/player/media_player_round_controller.cpp
<(src_loc)/media/player/media_player_round_controller.h
<(src_loc)/media/player/media_player_volume_controller.cpp
<(src_loc)/media/player/media_player_volume_controller.h
<(src_loc)/media/player/media_player_widget.cpp