From 124312357992d599cbbc60495491cc0cd3fd4505 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 10 Dec 2019 17:06:22 +0300 Subject: [PATCH] Allow multiple players of the same file. --- Telegram/SourceFiles/data/data_session.cpp | 30 ++++++-- Telegram/SourceFiles/data/data_session.h | 7 ++ .../history/view/media/history_view_gif.cpp | 59 +++++++------- .../history/view/media/history_view_gif.h | 4 +- .../streaming/media_streaming_document.cpp | 8 +- .../streaming/media_streaming_document.h | 2 - .../media/view/media_view_overlay_widget.cpp | 77 ++++++++++--------- 7 files changed, 105 insertions(+), 82 deletions(-) diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index f613c854a..a469f48b5 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_instance.h" // instance()->play() #include "media/streaming/media_streaming_loader.h" // unique_ptr #include "media/streaming/media_streaming_reader.h" // make_shared +#include "media/streaming/media_streaming_document.h" // make_shared PinnedDialogsCountMaxValue( }); } +template bool PruneDestroyedAndSet( base::flat_map< not_null, - std::weak_ptr<::Media::Streaming::Reader>> &readers, + std::weak_ptr> &objects, not_null document, - const std::shared_ptr<::Media::Streaming::Reader> &reader) { + const std::shared_ptr &object) { auto result = false; - for (auto i = begin(readers); i != end(readers);) { + for (auto i = begin(objects); i != end(objects);) { if (i->first == document) { - (i++)->second = reader; + (i++)->second = object; result = true; } else if (i->second.lock() != nullptr) { ++i; } else { - i = readers.erase(i); + i = objects.erase(i); } } return result; @@ -1156,6 +1158,24 @@ std::shared_ptr<::Media::Streaming::Reader> Session::documentStreamedReader( return result; } +std::shared_ptr<::Media::Streaming::Document> Session::documentStreamer( + not_null document, + FileOrigin origin) { + const auto i = _streamedDocuments.find(document); + if (i != end(_streamedDocuments)) { + if (auto result = i->second.lock()) { + return result; + } + } + auto result = std::make_shared<::Media::Streaming::Document>( + document, + origin); + if (!PruneDestroyedAndSet(_streamedDocuments, document, result)) { + _streamedDocuments.emplace_or_assign(document, result); + } + return result; +} + void Session::requestPollViewRepaint(not_null poll) { if (const auto i = _pollViews.find(poll); i != _pollViews.end()) { for (const auto view : i->second) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 0dac99603..a2392f21d 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -43,6 +43,7 @@ class Reader; } // namespace Clip namespace Streaming { class Reader; +class Document; } // namespace Streaming } // namespace Media @@ -439,6 +440,9 @@ public: not_null document, FileOrigin origin, bool forceRemoteLoader = false); + std::shared_ptr<::Media::Streaming::Document> documentStreamer( + not_null document, + FileOrigin origin); HistoryItem *addNewMessage( const MTPMessage &data, @@ -956,6 +960,9 @@ private: base::flat_map< not_null, std::weak_ptr<::Media::Streaming::Reader>> _streamedReaders; + base::flat_map< + not_null, + std::weak_ptr<::Media::Streaming::Document>> _streamedDocuments; base::flat_map> _folders; //rpl::variable _defaultFeedId = FeedId(); // #feed diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 4d2d6b40a..22519b4fc 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/clip/media_clip_reader.h" #include "media/player/media_player_instance.h" -#include "media/streaming/media_streaming_player.h" +#include "media/streaming/media_streaming_document.h" #include "media/view/media_view_playback_progress.h" #include "boxes/confirm_box.h" #include "history/history_item_components.h" @@ -46,17 +46,24 @@ int gifMaxStatusWidth(DocumentData *document) { struct Gif::Streamed { Streamed( - not_null owner, - std::shared_ptr<::Media::Streaming::Reader> reader); + not_null document, + Data::FileOrigin origin); + ~Streamed(); - ::Media::Streaming::Player player; - ::Media::Streaming::Information info; + std::shared_ptr<::Media::Streaming::Document> shared; + not_null<::Media::Streaming::Instance*> instance; + rpl::lifetime lifetime; }; Gif::Streamed::Streamed( - not_null owner, - std::shared_ptr<::Media::Streaming::Reader> reader) -: player(owner, std::move(reader)) { + not_null document, + Data::FileOrigin origin) +: shared(document->owner().documentStreamer(document, origin)) +, instance(shared->addInstance()) { +} + +Gif::Streamed::~Streamed() { + shared->removeInstance(instance); } Gif::Gif( @@ -378,7 +385,7 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); } - if (radial || (!player && ((_streamed && _streamed->player.failed()) || (!_data->loaded() && !_data->loading()) || !autoplayEnabled()))) { + if (radial || (!player && ((_streamed && _streamed->shared->player().failed()) || (!_data->loaded() && !_data->loading()) || !autoplayEnabled()))) { auto radialOpacity = (radial && _data->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); @@ -814,15 +821,15 @@ int Gif::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply return ::Media::Player::instance()->roundVideoPlayer(_parent->data()); } -::Media::Streaming::Player *Gif::activeOwnPlayer() const { +const ::Media::Streaming::Player *Gif::activeOwnPlayer() const { return (_streamed - && _streamed->player.ready() - && !_streamed->player.videoSize().isEmpty()) - ? &_streamed->player + && _streamed->shared->player().ready() + && !_streamed->shared->player().videoSize().isEmpty()) + ? &_streamed->shared->player() : nullptr; } -::Media::Streaming::Player *Gif::activeCurrentPlayer() const { +const ::Media::Streaming::Player *Gif::activeCurrentPlayer() const { if (const auto player = activeRoundPlayer()) { return player; } @@ -890,28 +897,24 @@ void Gif::playAnimation(bool autoplay) { options.mode = ::Media::Streaming::Mode::Video; options.loop = true; //} - _streamed->player.play(options); + _streamed->shared->play(options); } } void Gif::createStreamedPlayer() { - setStreamed(std::make_unique( - &_data->owner(), - _data->owner().documentStreamedReader( - _data, - _realParent->fullId()))); + setStreamed(std::make_unique(_data, _realParent->fullId())); - _streamed->player.updates( + _streamed->shared->player().updates( ) | rpl::start_with_next_error([=](::Media::Streaming::Update &&update) { handleStreamingUpdate(std::move(update)); }, [=](::Media::Streaming::Error &&error) { handleStreamingError(std::move(error)); - }, _streamed->player.lifetime()); + }, _streamed->lifetime); - _streamed->player.fullInCache( + _streamed->shared->player().fullInCache( ) | rpl::start_with_next([=](bool fullInCache) { _data->setLoadedInMediaCache(fullInCache); - }, _streamed->player.lifetime()); + }, _streamed->lifetime); } void Gif::setStreamed(std::unique_ptr value) { @@ -932,10 +935,8 @@ void Gif::handleStreamingUpdate(::Media::Streaming::Update &&update) { update.data.match([&](Information &update) { streamingReady(std::move(update)); }, [&](const PreloadedVideo &update) { - _streamed->info.video.state.receivedTill = update.till; //updatePlaybackState(); }, [&](const UpdateVideo &update) { - _streamed->info.video.state.position = update.position; history()->owner().requestViewRepaint(_parent); Core::App().updateNonIdle(); //updatePlaybackState(); @@ -949,11 +950,6 @@ void Gif::handleStreamingUpdate(::Media::Streaming::Update &&update) { //playbackWaitingChange(update.waiting); }, [&](MutedByOther) { }, [&](Finished) { - const auto finishTrack = [](TrackState &state) { - state.position = state.receivedTill = state.duration; - }; - finishTrack(_streamed->info.audio.state); - finishTrack(_streamed->info.video.state); //updatePlaybackState(); }); } @@ -974,7 +970,6 @@ void Gif::handleStreamingError(::Media::Streaming::Error &&error) { } void Gif::streamingReady(::Media::Streaming::Information &&info) { - _streamed->info = std::move(info); history()->owner().requestViewResize(_parent); //validateStreamedGoodThumbnail(); //if (videoShown()) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index 65e0c346c..e1db93437 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -94,8 +94,8 @@ private: QSize countCurrentSize(int newWidth) override; QSize videoSize() const; ::Media::Streaming::Player *activeRoundPlayer() const; - ::Media::Streaming::Player *activeOwnPlayer() const; - ::Media::Streaming::Player *activeCurrentPlayer() const; + const ::Media::Streaming::Player *activeOwnPlayer() const; + const ::Media::Streaming::Player *activeCurrentPlayer() const; ::Media::View::PlaybackProgress *videoPlayback() const; void createStreamedPlayer(); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp index e967f363e..06f3672d9 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_document.cpp @@ -51,12 +51,12 @@ Document::Document( handleUpdate(std::move(update)); }, [=](Streaming::Error &&error) { handleError(std::move(error)); - }, lifetime()); + }, _player.lifetime()); _player.fullInCache( ) | rpl::start_with_next([=](bool fullInCache) { _document->setLoadedInMediaCache(fullInCache); - }, lifetime()); + }, _player.lifetime()); } const Player &Document::player() const { @@ -124,10 +124,6 @@ Ui::RadialState Document::waitingState() const { return _radial.computeState(); } -rpl::lifetime &Document::lifetime() { - return _player.lifetime(); -} - void Document::handleUpdate(Update &&update) { update.data.match([&](Information &update) { ready(std::move(update)); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_document.h b/Telegram/SourceFiles/media/streaming/media_streaming_document.h index 8e5febb0c..fa1e30e03 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_document.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_document.h @@ -53,8 +53,6 @@ public: [[nodiscard]] float64 waitingOpacity() const; [[nodiscard]] Ui::RadialState waitingState() const; - [[nodiscard]] rpl::lifetime &lifetime(); - private: void waitingCallback(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 5bab7bd67..44898993f 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -190,8 +190,9 @@ struct OverlayWidget::Streamed { QWidget *controlsParent, not_null controlsDelegate, Callback &&loadingCallback); + ~Streamed(); - Streaming::Document document; + std::shared_ptr shared; not_null instance; PlaybackControls controls; @@ -200,6 +201,8 @@ struct OverlayWidget::Streamed { bool withSound = false; bool pausedBySeek = false; bool resumeOnCallEnd = false; + + rpl::lifetime lifetime; }; template @@ -209,12 +212,16 @@ OverlayWidget::Streamed::Streamed( QWidget *controlsParent, not_null controlsDelegate, Callback &&loadingCallback) -: document(document, origin) -, instance(this->document.addInstance()) +: shared(document->owner().documentStreamer(document, origin)) +, instance(shared->addInstance()) , controls(controlsParent, controlsDelegate) { instance->setWaitingCallback(std::forward(loadingCallback)); } +OverlayWidget::Streamed::~Streamed() { + shared->removeInstance(instance); +} + OverlayWidget::OverlayWidget() : OverlayParent(nullptr) , _transparentBrush(style::transparentPlaceholderBrush()) @@ -360,13 +367,13 @@ void OverlayWidget::moveToScreen(bool force) { } bool OverlayWidget::videoShown() const { - return _streamed && !_streamed->document.info().video.cover.isNull(); + return _streamed && !_streamed->shared->info().video.cover.isNull(); } QSize OverlayWidget::videoSize() const { Expects(videoShown()); - return _streamed->document.info().video.size; + return _streamed->shared->info().video.size; } bool OverlayWidget::videoIsGifv() const { @@ -380,9 +387,9 @@ QImage OverlayWidget::videoFrame() const { //request.radius = (_doc && _doc->isVideoMessage()) // ? ImageRoundRadius::Ellipse // : ImageRoundRadius::None; - return _streamed->document.player().ready() - ? _streamed->document.player().frame(request) - : _streamed->document.info().video.cover; + return _streamed->shared->player().ready() + ? _streamed->shared->player().frame(request) + : _streamed->shared->info().video.cover; } QImage OverlayWidget::videoFrameForDirectPaint() const { @@ -2000,12 +2007,12 @@ void OverlayWidget::initStreaming() { Core::App().updateNonIdle(); - _streamed->document.player().updates( + _streamed->shared->player().updates( ) | rpl::start_with_next_error([=](Streaming::Update &&update) { handleStreamingUpdate(std::move(update)); }, [=](Streaming::Error &&error) { handleStreamingError(std::move(error)); - }, _streamed->document.lifetime()); + }, _streamed->lifetime); restartAtSeekPosition(0); } @@ -2085,14 +2092,14 @@ void OverlayWidget::createStreamingObjects() { QImage OverlayWidget::transformVideoFrame(QImage frame) const { Expects(videoShown()); - if (_streamed->document.info().video.rotation != 0) { + if (_streamed->shared->info().video.rotation != 0) { auto transform = QTransform(); - transform.rotate(_streamed->document.info().video.rotation); + transform.rotate(_streamed->shared->info().video.rotation); frame = frame.transformed(transform); } - if (frame.size() != _streamed->document.info().video.size) { + if (frame.size() != _streamed->shared->info().video.size) { frame = frame.scaled( - _streamed->document.info().video.size, + _streamed->shared->info().video.size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } @@ -2269,18 +2276,18 @@ void OverlayWidget::playbackPauseResume() { Expects(_streamed != nullptr); _streamed->resumeOnCallEnd = false; - if (_streamed->document.player().failed()) { + if (_streamed->shared->player().failed()) { clearStreaming(); initStreaming(); - } else if (_streamed->document.player().finished()) { + } else if (_streamed->shared->player().finished()) { _streamingStartPaused = false; restartAtSeekPosition(0); - } else if (_streamed->document.player().paused()) { - _streamed->document.resume(); + } else if (_streamed->shared->player().paused()) { + _streamed->shared->resume(); updatePlaybackState(); playbackPauseMusic(); } else { - _streamed->document.pause(); + _streamed->shared->pause(); updatePlaybackState(); } } @@ -2290,7 +2297,7 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) { Expects(_doc != nullptr); if (videoShown()) { - _streamed->document.saveFrameToCover(); + _streamed->shared->saveFrameToCover(); _current = Images::PixmapFast(transformVideoFrame(videoFrame())); update(contentRect()); } @@ -2301,9 +2308,9 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) { options.mode = Streaming::Mode::Video; options.loop = true; } - _streamed->document.play(options); + _streamed->shared->play(options); if (_streamingStartPaused) { - _streamed->document.pause(); + _streamed->shared->pause(); } else { playbackPauseMusic(); } @@ -2315,8 +2322,8 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) { void OverlayWidget::playbackControlsSeekProgress(crl::time position) { Expects(_streamed != nullptr); - if (!_streamed->document.player().paused() - && !_streamed->document.player().finished()) { + if (!_streamed->shared->player().paused() + && !_streamed->shared->player().finished()) { _streamed->pausedBySeek = true; playbackControlsPause(); } @@ -2326,7 +2333,7 @@ void OverlayWidget::playbackControlsSeekFinished(crl::time position) { Expects(_streamed != nullptr); _streamingStartPaused = !_streamed->pausedBySeek - && !_streamed->document.player().finished(); + && !_streamed->shared->player().finished(); restartAtSeekPosition(position); } @@ -2364,12 +2371,12 @@ void OverlayWidget::playbackToggleFullScreen() { void OverlayWidget::playbackPauseOnCall() { Expects(_streamed != nullptr); - if (_streamed->document.player().finished() - || _streamed->document.player().paused()) { + if (_streamed->shared->player().finished() + || _streamed->shared->player().paused()) { return; } _streamed->resumeOnCallEnd = true; - _streamed->document.pause(); + _streamed->shared->pause(); updatePlaybackState(); } @@ -2378,7 +2385,7 @@ void OverlayWidget::playbackResumeOnCall() { if (_streamed->resumeOnCallEnd) { _streamed->resumeOnCallEnd = false; - _streamed->document.resume(); + _streamed->shared->resume(); updatePlaybackState(); playbackPauseMusic(); } @@ -2400,7 +2407,7 @@ void OverlayWidget::updatePlaybackState() { if (videoIsGifv()) { return; } - const auto state = _streamed->document.player().prepareLegacyState(); + const auto state = _streamed->shared->player().prepareLegacyState(); if (state.position != kTimeUnknown && state.length != kTimeUnknown) { _streamed->controls.updatePlayback(state); } @@ -2704,7 +2711,7 @@ void OverlayWidget::paintEvent(QPaintEvent *e) { void OverlayWidget::checkGroupThumbsAnimation() { if (_groupThumbs - && (!_streamed || _streamed->document.player().ready())) { + && (!_streamed || _streamed->shared->player().ready())) { _groupThumbs->checkForAnimationStart(); } } @@ -2716,7 +2723,7 @@ void OverlayWidget::paintTransformedVideoFrame(Painter &p) { // const auto fill = rect.intersected(this->rect()); // PaintImageProfile(p, image, rect, fill); //} else { - const auto rotation = _streamed->document.info().video.rotation; + const auto rotation = _streamed->shared->info().video.rotation; const auto rotated = [](QRect rect, int rotation) { switch (rotation) { case 0: return rect; @@ -2756,7 +2763,7 @@ void OverlayWidget::paintRadialLoading( bool radial, float64 radialOpacity) { if (_streamed) { - if (!_streamed->document.waitingShown()) { + if (!_streamed->shared->waitingShown()) { return; } } else if (!radial && (!_doc || _doc->loaded())) { @@ -2809,11 +2816,11 @@ void OverlayWidget::paintRadialLoadingContent( if (_streamed) { paintBg( - _streamed->document.waitingOpacity(), + _streamed->shared->waitingOpacity(), st::radialBg); Ui::InfiniteRadialAnimation::Draw( p, - _streamed->document.waitingState(), + _streamed->shared->waitingState(), arc.topLeft(), arc.size(), width(),