diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index d1ceecc03..2ba5a4787 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -415,9 +415,21 @@ void Gif::draw(Painter &p, const PaintContext &context) const { activeOwnPlaying->frozenFrame = QImage(); activeOwnPlaying->frozenStatusText = QString(); } - p.drawImage(rthumb, streamed->frame(request)); - if (!paused) { - streamed->markFrameShown(); + + const auto frame = streamed->frameWithInfo(request); + const auto playOnce = sticker + && !Core::App().settings().loopAnimatedStickers(); + const auto switchToNext = !playOnce + || (frame.index != 0) + || !_stickerOncePlayed; + p.drawImage(rthumb, frame.image); + if (!paused + && switchToNext + && streamed->markFrameShown() + && playOnce + && !_stickerOncePlayed) { + _stickerOncePlayed = true; + _parent->delegate()->elementStartStickerLoop(_parent); } } diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index b7abc70c5..911d738cb 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -101,6 +101,10 @@ public: QPoint resolveCustomInfoRightBottom() const override; QString additionalInfoString() const override; + void stickerClearLoopPlayed() override { + _stickerOncePlayed = false; + } + bool skipBubbleTail() const override { return isRoundedInBubbleBottom() && _caption.isEmpty(); } @@ -192,6 +196,7 @@ private: ClickHandlerPtr _stickerLink; QString _downloadSize; + mutable bool _stickerOncePlayed = false; }; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_common.h b/Telegram/SourceFiles/media/streaming/media_streaming_common.h index 47bd097dd..752a737a8 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_common.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_common.h @@ -177,7 +177,7 @@ struct FrameYUV420 { }; struct FrameWithInfo { - QImage original; + QImage image; FrameYUV420 *yuv420 = nullptr; FrameFormat format = FrameFormat::None; int index = -1; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp index d2071f833..898bf7678 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp @@ -172,6 +172,10 @@ QImage Instance::frame(const FrameRequest &request) const { return player().frame(request, this); } +FrameWithInfo Instance::frameWithInfo(const FrameRequest &request) const { + return player().frameWithInfo(request, this); +} + FrameWithInfo Instance::frameWithInfo() const { return player().frameWithInfo(this); } diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_instance.h b/Telegram/SourceFiles/media/streaming/media_streaming_instance.h index 63e2ec743..c391244f9 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_instance.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_instance.h @@ -69,6 +69,8 @@ public: void callWaitingCallback(); [[nodiscard]] QImage frame(const FrameRequest &request) const; + [[nodiscard]] FrameWithInfo frameWithInfo( + const FrameRequest &request) const; [[nodiscard]] FrameWithInfo frameWithInfo() const; bool markFrameShown() const; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 39cfff81f..3a60b39a0 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -884,6 +884,14 @@ QImage Player::frame( return _video->frame(request, instance); } +FrameWithInfo Player::frameWithInfo( + const FrameRequest &request, + const Instance *instance) const { + Expects(_video != nullptr); + + return _video->frameWithInfo(request, instance); +} + FrameWithInfo Player::frameWithInfo(const Instance *instance) const { Expects(_video != nullptr); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.h b/Telegram/SourceFiles/media/streaming/media_streaming_player.h index 0cfd285a8..08ff3777a 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.h @@ -64,7 +64,9 @@ public: [[nodiscard]] QImage frame( const FrameRequest &request, const Instance *instance = nullptr) const; - + [[nodiscard]] FrameWithInfo frameWithInfo( + const FrameRequest &request, + const Instance *instance = nullptr) const; [[nodiscard]] FrameWithInfo frameWithInfo( const Instance *instance = nullptr) const; // !requireARGB32 diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp index c6ab1d97a..80d69d33e 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp @@ -154,6 +154,7 @@ private: Fn _error; crl::time _pausedTime = kTimeUnknown; crl::time _resumedTime = kTimeUnknown; + int _frameIndex = 0; int _durationByLastPacket = 0; mutable TimePoint _syncTimePoint; crl::time _loopingShift = 0; @@ -330,6 +331,7 @@ bool VideoTrackObject::loopAround() { return false; } avcodec_flush_buffers(_stream.codec.get()); + _frameIndex = 0; _loopingShift += duration; _readTillEnd = false; return true; @@ -372,6 +374,7 @@ auto VideoTrackObject::readFrame(not_null frame) -> FrameResult { return FrameResult::Error; } std::swap(frame->decoded, _stream.frame); + frame->index = _frameIndex++; frame->position = position; frame->displayed = kTimeUnknown; return FrameResult::Done; @@ -651,6 +654,7 @@ void VideoTrackObject::callReady() { Expects(_ready != nullptr); const auto frame = _shared->frameForPaint(); + ++_frameIndex; auto data = VideoInformation(); data.size = FFmpeg::CorrectByAspect( @@ -961,9 +965,6 @@ bool VideoTrack::Shared::markFrameShown() { if (frame->displayed == kTimeUnknown) { return false; } - if (counter == 2 * kFramesCount - 1) { - ++_counterCycle; - } _counter.store( next, std::memory_order_release); @@ -995,7 +996,7 @@ VideoTrack::FrameWithIndex VideoTrack::Shared::frameForPaintWithIndex() { Assert(frame->displayed != kTimeUnknown); return { .frame = frame, - .index = (_counterCycle * 2 * kFramesCount) + index, + .index = frame->index, }; } @@ -1101,7 +1102,44 @@ bool VideoTrack::markFrameShown() { QImage VideoTrack::frame( const FrameRequest &request, const Instance *instance) { - const auto frame = _shared->frameForPaint(); + return frameImage(_shared->frameForPaint(), request, instance); +} + +FrameWithInfo VideoTrack::frameWithInfo( + const FrameRequest &request, + const Instance *instance) { + const auto data = _shared->frameForPaintWithIndex(); + return { + .image = frameImage(data.frame, request, instance), + .format = FrameFormat::ARGB32, + .index = data.index, + }; +} + +FrameWithInfo VideoTrack::frameWithInfo(const Instance *instance) { + const auto data = _shared->frameForPaintWithIndex(); + const auto i = data.frame->prepared.find(instance); + const auto none = (i == data.frame->prepared.end()); + if (none || i->second.request.requireARGB32) { + _wrapped.with([=](Implementation &unwrapped) { + unwrapped.updateFrameRequest( + instance, + { .requireARGB32 = false }); + }); + } + return { + .image = data.frame->original, + .yuv420 = &data.frame->yuv420, + .format = data.frame->format, + .index = data.index, + .alpha = data.frame->alpha, + }; +} + +QImage VideoTrack::frameImage( + not_null frame, + const FrameRequest &request, + const Instance *instance) { const auto i = frame->prepared.find(instance); const auto none = (i == frame->prepared.end()); const auto preparedFor = frame->prepared.empty() @@ -1151,26 +1189,6 @@ QImage VideoTrack::frame( return i->second.image; } -FrameWithInfo VideoTrack::frameWithInfo(const Instance *instance) { - const auto data = _shared->frameForPaintWithIndex(); - const auto i = data.frame->prepared.find(instance); - const auto none = (i == data.frame->prepared.end()); - if (none || i->second.request.requireARGB32) { - _wrapped.with([=](Implementation &unwrapped) { - unwrapped.updateFrameRequest( - instance, - { .requireARGB32 = false }); - }); - } - return { - .original = data.frame->original, - .yuv420 = &data.frame->yuv420, - .format = data.frame->format, - .index = data.index, - .alpha = data.frame->alpha, - }; -} - QImage VideoTrack::currentFrameImage() { const auto frame = _shared->frameForPaint(); if (frame->original.isNull() && frame->format == FrameFormat::YUV420) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h index 484b4a24a..98a5fcfed 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h @@ -58,6 +58,9 @@ public: [[nodiscard]] QImage frame( const FrameRequest &request, const Instance *instance); + [[nodiscard]] FrameWithInfo frameWithInfo( + const FrameRequest &request, + const Instance *instance); [[nodiscard]] FrameWithInfo frameWithInfo(const Instance *instance); [[nodiscard]] QImage currentFrameImage(); void unregisterInstance(not_null instance); @@ -88,6 +91,7 @@ private: base::flat_map prepared; + int index = 0; bool alpha = false; }; struct FrameWithIndex { @@ -141,9 +145,6 @@ private: static constexpr auto kCounterUninitialized = -1; std::atomic _counter = kCounterUninitialized; - // Main thread. - int _counterCycle = 0; - static constexpr auto kFramesCount = 4; std::array _frames; @@ -160,6 +161,11 @@ private: not_null frame, crl::time trackTime); + [[nodiscard]] QImage frameImage( + not_null frame, + const FrameRequest &request, + const Instance *instance); + const int _streamIndex = 0; const AVRational _streamTimeBase; const crl::time _streamDuration = 0; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index f07f0cecd..a1e13b50c 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -188,9 +188,9 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame( if (data.format == Streaming::FrameFormat::None) { return; } else if (data.format == Streaming::FrameFormat::ARGB32) { - Assert(!data.original.isNull()); + Assert(!data.image.isNull()); paintTransformedStaticContent( - data.original, + data.image, geometry, data.alpha, data.alpha); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 043875e2e..ba4fb5871 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -610,7 +610,7 @@ Streaming::FrameWithInfo OverlayWidget::videoFrameWithInfo() const { return _streamed->instance.player().ready() ? _streamed->instance.frameWithInfo() : Streaming::FrameWithInfo{ - .original = _streamed->instance.info().video.cover, + .image = _streamed->instance.info().video.cover, .format = Streaming::FrameFormat::ARGB32, .index = -2, .alpha = _streamed->instance.info().video.alpha, diff --git a/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp index 813b159ad..71c462a1c 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip_opengl.cpp @@ -285,8 +285,8 @@ void Pip::RendererGL::paintTransformedVideoFrame( } geometry.rotation = (geometry.rotation + geometry.videoRotation) % 360; if (data.format == Streaming::FrameFormat::ARGB32) { - Assert(!data.original.isNull()); - paintTransformedStaticContent(data.original, geometry); + Assert(!data.image.isNull()); + paintTransformedStaticContent(data.image, geometry); return; } Assert(data.format == Streaming::FrameFormat::YUV420);