From f6f0b023337dbe383ec0dd27d9665d5f5fd11d9f Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 3 Jun 2021 16:57:48 +0400 Subject: [PATCH] Render YUV420 video in media viewer. --- .../media/streaming/media_streaming_common.h | 33 ++- .../streaming/media_streaming_instance.cpp | 4 + .../streaming/media_streaming_instance.h | 1 + .../streaming/media_streaming_player.cpp | 6 + .../media/streaming/media_streaming_player.h | 4 + .../streaming/media_streaming_utility.cpp | 13 ++ .../media/streaming/media_streaming_utility.h | 1 + .../streaming/media_streaming_video_track.cpp | 107 ++++++++-- .../streaming/media_streaming_video_track.h | 11 + .../media/view/media_view_overlay_opengl.cpp | 195 +++++++++++++++--- .../media/view/media_view_overlay_opengl.h | 15 +- .../media/view/media_view_overlay_raster.cpp | 3 + .../media/view/media_view_overlay_raster.h | 2 + .../media/view/media_view_overlay_renderer.h | 2 + .../media/view/media_view_overlay_widget.cpp | 23 ++- .../media/view/media_view_overlay_widget.h | 4 + 16 files changed, 370 insertions(+), 54 deletions(-) diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_common.h b/Telegram/SourceFiles/media/streaming/media_streaming_common.h index fd564e02b..d7a92c4ce 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_common.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_common.h @@ -119,6 +119,7 @@ struct FrameRequest { QSize outer; ImageRoundRadius radius = ImageRoundRadius(); RectParts corners = RectPart::AllCorners; + bool requireARGB32 = true; bool strict = true; static FrameRequest NonStrict() { @@ -135,16 +136,44 @@ struct FrameRequest { return (resize == other.resize) && (outer == other.outer) && (radius == other.radius) - && (corners == other.corners); + && (corners == other.corners) + && (requireARGB32 == other.requireARGB32); } [[nodiscard]] bool operator!=(const FrameRequest &other) const { return !(*this == other); } [[nodiscard]] bool goodFor(const FrameRequest &other) const { - return (*this == other) || (strict && !other.strict); + return (requireARGB32 == other.requireARGB32) + && ((*this == other) || (strict && !other.strict)); } }; +enum class FrameFormat { + None, + ARGB32, + YUV420, +}; + +struct FrameChannel { + const void *data = nullptr; + int stride = 0; +}; + +struct FrameYUV420 { + QSize size; + QSize chromaSize; + FrameChannel y; + FrameChannel u; + FrameChannel v; +}; + +struct FrameWithInfo { + QImage original; + FrameYUV420 *yuv420 = nullptr; + FrameFormat format = FrameFormat::None; + int index = -1; +}; + } // namespace Streaming } // namespace Media diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_instance.cpp index 782d7c40a..fefbeb819 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 { + return player().frameWithInfo(this); +} + bool Instance::markFrameShown() { Expects(_shared != nullptr); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_instance.h b/Telegram/SourceFiles/media/streaming/media_streaming_instance.h index a92b0c87e..ad5bd0559 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_instance.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_instance.h @@ -69,6 +69,7 @@ public: void callWaitingCallback(); [[nodiscard]] QImage frame(const FrameRequest &request) const; + [[nodiscard]] FrameWithInfo frameWithInfo() const; bool markFrameShown(); void lockPlayer(); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 9a58d368d..f94637f1c 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -881,6 +881,12 @@ QImage Player::frame( return _video->frame(request, instance); } +FrameWithInfo Player::frameWithInfo(const Instance *instance) const { + Expects(_video != nullptr); + + return _video->frameWithInfo(instance); +} + void Player::unregisterInstance(not_null instance) { if (_video) { _video->unregisterInstance(instance); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.h b/Telegram/SourceFiles/media/streaming/media_streaming_player.h index cd4c15a9b..5249116a4 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.h @@ -64,6 +64,10 @@ public: [[nodiscard]] QImage frame( const FrameRequest &request, const Instance *instance = nullptr) const; + + [[nodiscard]] FrameWithInfo frameWithInfo( + const Instance *instance = nullptr) const; // !requireARGB32 + void unregisterInstance(not_null instance); bool markFrameShown(); diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp index 5cde41782..444262ff4 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp @@ -174,6 +174,19 @@ QImage ConvertFrame( return storage; } +FrameYUV420 ExtractYUV420(Stream &stream, AVFrame *frame) { + return { + .size = { frame->width, frame->height }, + .chromaSize = { + AV_CEIL_RSHIFT(frame->width, 1), // SWScale does that. + AV_CEIL_RSHIFT(frame->height, 1) + }, + .y = { .data = frame->data[0], .stride = frame->linesize[0] }, + .u = { .data = frame->data[1], .stride = frame->linesize[1] }, + .v = { .data = frame->data[2], .stride = frame->linesize[2] }, + }; +} + void PaintFrameOuter(QPainter &p, const QRect &inner, QSize outer) { const auto left = inner.x(); const auto right = outer.width() - inner.width() - left; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_utility.h b/Telegram/SourceFiles/media/streaming/media_streaming_utility.h index 3a39eb7f9..6a0769c26 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_utility.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_utility.h @@ -58,6 +58,7 @@ struct Stream { AVFrame *frame, QSize resize, QImage storage); +[[nodiscard]] FrameYUV420 ExtractYUV420(Stream &stream, AVFrame *frame); [[nodiscard]] QImage PrepareByRequest( const QImage &original, bool alpha, diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp index 07912c141..da11accd8 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp @@ -53,6 +53,7 @@ public: void removeFrameRequest(const Instance *instance); void rasterizeFrame(not_null frame); + [[nodiscard]] bool requireARGB32() const; private: enum class FrameResult { @@ -357,20 +358,50 @@ QSize VideoTrackObject::chooseOriginalResize() const { return chosen; } +bool VideoTrackObject::requireARGB32() const { + for (const auto &[_, request] : _requests) { + if (!request.requireARGB32) { + return false; + } + } + return true; +} + void VideoTrackObject::rasterizeFrame(not_null frame) { Expects(frame->position != kFinishedPosition); fillRequests(frame); - frame->alpha = (frame->decoded->format == AV_PIX_FMT_BGRA); - frame->original = ConvertFrame( - _stream, - frame->decoded.get(), - chooseOriginalResize(), - std::move(frame->original)); - if (frame->original.isNull()) { - frame->prepared.clear(); - fail(Error::InvalidData); - return; + frame->format = FrameFormat::None; + if (frame->decoded->format == AV_PIX_FMT_YUV420P && !requireARGB32()) { + frame->alpha = false; + frame->yuv420 = ExtractYUV420(_stream, frame->decoded.get()); + if (frame->yuv420.size.isEmpty() + || frame->yuv420.chromaSize.isEmpty() + || !frame->yuv420.y.data + || !frame->yuv420.u.data + || !frame->yuv420.v.data) { + frame->prepared.clear(); + fail(Error::InvalidData); + return; + } + frame->format = FrameFormat::YUV420; + } else { + frame->alpha = (frame->decoded->format == AV_PIX_FMT_BGRA); + frame->yuv420.size = { + frame->decoded->width, + frame->decoded->height + }; + frame->original = ConvertFrame( + _stream, + frame->decoded.get(), + chooseOriginalResize(), + std::move(frame->original)); + if (frame->original.isNull()) { + frame->prepared.clear(); + fail(Error::InvalidData); + return; + } + frame->format = FrameFormat::ARGB32; } VideoTrack::PrepareFrameByRequests(frame, _stream.rotation); @@ -613,6 +644,7 @@ void VideoTrack::Shared::init(QImage &&cover, crl::time position) { _frames[0].original = std::move(cover); _frames[0].position = position; + _frames[0].format = FrameFormat::ARGB32; // Usually main thread sets displayed time before _counter increment. // But in this case we update _counter, so we set a fake displayed time. @@ -722,7 +754,7 @@ auto VideoTrack::Shared::presentFrame( // Release this frame to the main thread for rendering. _counter.store( - (counter + 1) % (2 * kFramesCount), + counter + 1, std::memory_order_release); return { position, crl::time(0), addedWorldTimeDelay }; }; @@ -847,6 +879,9 @@ bool VideoTrack::Shared::markFrameShown() { if (frame->displayed == kTimeUnknown) { return false; } + if (counter == 2 * kFramesCount - 1) { + ++_counterCycle; + } _counter.store( next, std::memory_order_release); @@ -867,12 +902,20 @@ bool VideoTrack::Shared::markFrameShown() { } not_null VideoTrack::Shared::frameForPaint() { - const auto result = getFrame(counter() / 2); - Assert(!result->original.isNull()); - Assert(result->position != kTimeUnknown); - Assert(result->displayed != kTimeUnknown); + return frameForPaintWithIndex().frame; +} + +VideoTrack::FrameWithIndex VideoTrack::Shared::frameForPaintWithIndex() { + const auto index = counter() / 2; + const auto frame = getFrame(index); + Assert(frame->format != FrameFormat::None); + Assert(frame->position != kTimeUnknown); + Assert(frame->displayed != kTimeUnknown); + return { + .frame = frame, + .index = (_counterCycle * 2 * kFramesCount) + index, + }; - return result; } VideoTrack::VideoTrack( @@ -977,7 +1020,8 @@ bool VideoTrack::markFrameShown() { QImage VideoTrack::frame( const FrameRequest &request, const Instance *instance) { - const auto frame = _shared->frameForPaint(); + const auto data = _shared->frameForPaintWithIndex(); + const auto frame = data.frame; const auto i = frame->prepared.find(instance); const auto none = (i == frame->prepared.end()); const auto preparedFor = frame->prepared.empty() @@ -1020,6 +1064,25 @@ 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, + }; +} + void VideoTrack::unregisterInstance(not_null instance) { _wrapped.with([=](Implementation &unwrapped) { unwrapped.removeFrameRequest(instance); @@ -1029,7 +1092,12 @@ void VideoTrack::unregisterInstance(not_null instance) { void VideoTrack::PrepareFrameByRequests( not_null frame, int rotation) { - Expects(!frame->original.isNull()); + Expects(frame->format != FrameFormat::ARGB32 + || !frame->original.isNull()); + + if (frame->format != FrameFormat::ARGB32) { + return; + } const auto begin = frame->prepared.begin(); const auto end = frame->prepared.end(); @@ -1063,7 +1131,8 @@ bool VideoTrack::IsDecoded(not_null frame) { bool VideoTrack::IsRasterized(not_null frame) { return IsDecoded(frame) - && !frame->original.isNull(); + && (!frame->original.isNull() + || frame->format == FrameFormat::YUV420); } bool VideoTrack::IsStale(not_null frame, crl::time trackTime) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h index 880bb0acc..26413d094 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.h @@ -58,6 +58,7 @@ public: [[nodiscard]] QImage frame( const FrameRequest &request, const Instance *instance); + [[nodiscard]] FrameWithInfo frameWithInfo(const Instance *instance); void unregisterInstance(not_null instance); [[nodiscard]] rpl::producer<> checkNextFrame() const; [[nodiscard]] rpl::producer<> waitingForData() const; @@ -78,14 +79,20 @@ private: struct Frame { FFmpeg::FramePointer decoded = FFmpeg::MakeFramePointer(); QImage original; + FrameYUV420 yuv420; crl::time position = kTimeUnknown; crl::time displayed = kTimeUnknown; crl::time display = kTimeUnknown; + FrameFormat format = FrameFormat::None; base::flat_map prepared; bool alpha = false; }; + struct FrameWithIndex { + not_null frame; + int index = -1; + }; class Shared { public: @@ -123,6 +130,7 @@ private: bool markFrameShown(); [[nodiscard]] crl::time nextFrameDisplayTime() const; [[nodiscard]] not_null frameForPaint(); + [[nodiscard]] FrameWithIndex frameForPaintWithIndex(); private: [[nodiscard]] not_null getFrame(int index); @@ -132,6 +140,9 @@ private: static constexpr auto kCounterUninitialized = -1; std::atomic _counter = kCounterUninitialized; + // Main thread. + int _counterCycle = 0; + static constexpr auto kFramesCount = 4; std::array _frames; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index 2b9b0556f..a77013196 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/view/media_view_overlay_opengl.h" #include "ui/gl/gl_shader.h" +#include "media/streaming/media_streaming_common.h" #include "base/platform/base_platform_info.h" namespace Media::View { @@ -31,6 +32,23 @@ constexpr auto kFooterOffset = kSaveMsgOffset + 4; constexpr auto kCaptionOffset = kFooterOffset + 4; constexpr auto kGroupThumbsOffset = kCaptionOffset + 4; +[[nodiscard]] ShaderPart FragmentPlaceOnTransparentBackground() { + return { + .header = R"( +uniform vec4 transparentBg; +uniform vec4 transparentFg; +uniform float transparentSize; +)", + .body = R"( + vec2 checkboardLadder = floor(gl_FragCoord.xy / transparentSize); + float checkboard = mod(checkboardLadder.x + checkboardLadder.y, 2.0); + vec4 checkboardColor = checkboard * transparentBg + + (1. - checkboard) * transparentFg; + result += checkboardColor * (1. - result.a); +)", + }; +} + } // namespace OverlayWidget::RendererGL::RendererGL(not_null owner) @@ -59,7 +77,7 @@ void OverlayWidget::RendererGL::init( _textures.ensureCreated(f); _imageProgram.emplace(); - LinkProgram( + _texturedVertexShader = LinkProgram( &*_imageProgram, VertexShader({ VertexViewportTransform(), @@ -67,6 +85,23 @@ void OverlayWidget::RendererGL::init( }), FragmentShader({ FragmentSampleARGB32Texture(), + })).vertex; + + _withTransparencyProgram.emplace(); + LinkProgram( + &*_withTransparencyProgram, + _texturedVertexShader, + FragmentShader({ + FragmentSampleARGB32Texture(), + FragmentPlaceOnTransparentBackground(), + })); + + _yuv420Program.emplace(); + LinkProgram( + &*_yuv420Program, + _texturedVertexShader, + FragmentShader({ + FragmentSampleYUV420Texture(), })); _background.init(f); @@ -78,6 +113,9 @@ void OverlayWidget::RendererGL::deinit( _background.deinit(f); _textures.destroy(f); _imageProgram = std::nullopt; + _texturedVertexShader = nullptr; + _withTransparencyProgram = std::nullopt; + _yuv420Program = std::nullopt; _contentBuffer = std::nullopt; } @@ -138,11 +176,72 @@ void OverlayWidget::RendererGL::paintBackground() { void OverlayWidget::RendererGL::paintTransformedVideoFrame( QRect rect, int rotation) { - paintTransformedStaticContent( - _owner->videoFrame(), - rect, - rotation, - false); + const auto data = _owner->videoFrameWithInfo(); + if (data.format == Streaming::FrameFormat::None) { + return; + } + if (data.format == Streaming::FrameFormat::ARGB32) { + Assert(!data.original.isNull()); + paintTransformedStaticContent( + data.original, + rect, + rotation, + false); + return; + } + Assert(data.format == Streaming::FrameFormat::YUV420); + Assert(!data.yuv420->size.isEmpty()); + const auto yuv = data.yuv420; + _f->glUseProgram(_yuv420Program->programId()); + + const auto upload = (_trackFrameIndex != data.index) + || (_streamedIndex != _owner->streamedIndex()); + _trackFrameIndex = data.index; + _streamedIndex = _owner->streamedIndex(); + + _f->glActiveTexture(GL_TEXTURE0); + _textures.bind(*_f, 0); + if (upload) { + _f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + uploadTexture( + GL_RED, + GL_RED, + yuv->size, + _lumaSize, + yuv->y.stride, + yuv->y.data); + _lumaSize = yuv->size; + _rgbaSize = QSize(); + } + _f->glActiveTexture(GL_TEXTURE1); + _textures.bind(*_f, 1); + if (upload) { + uploadTexture( + GL_RED, + GL_RED, + yuv->chromaSize, + _chromaSize, + yuv->u.stride, + yuv->u.data); + } + _f->glActiveTexture(GL_TEXTURE2); + _textures.bind(*_f, 2); + if (upload) { + uploadTexture( + GL_RED, + GL_RED, + yuv->chromaSize, + _chromaSize, + yuv->v.stride, + yuv->v.data); + _chromaSize = yuv->chromaSize; + _f->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + } + _yuv420Program->setUniformValue("y_texture", GLint(0)); + _yuv420Program->setUniformValue("u_texture", GLint(1)); + _yuv420Program->setUniformValue("v_texture", GLint(2)); + + paintTransformedContent(&*_yuv420Program, rect, rotation); } void OverlayWidget::RendererGL::paintTransformedStaticContent( @@ -150,7 +249,47 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent( QRect rect, int rotation, bool fillTransparentBackground) { - AssertIsDebug(fillTransparentBackground); + auto &program = fillTransparentBackground + ? _withTransparencyProgram + : _imageProgram; + _f->glUseProgram(program->programId()); + if (fillTransparentBackground) { + program->setUniformValue( + "transparentBg", + Uniform(st::mediaviewTransparentBg->c)); + program->setUniformValue( + "transparentFg", + Uniform(st::mediaviewTransparentFg->c)); + program->setUniformValue( + "transparentSize", + st::transparentPlaceholderSize * _factor); + } + + _f->glActiveTexture(GL_TEXTURE0); + _textures.bind(*_f, 0); + const auto cacheKey = image.cacheKey(); + const auto upload = (_cacheKey != cacheKey); + if (upload) { + const auto stride = image.bytesPerLine() / 4; + const auto data = image.constBits(); + uploadTexture( + GL_RGBA, + GL_RGBA, + image.size(), + _rgbaSize, + stride, + data); + _rgbaSize = image.size(); + _lumaSize = QSize(); + } + + paintTransformedContent(&*program, rect, rotation); +} + +void OverlayWidget::RendererGL::paintTransformedContent( + not_null program, + QRect rect, + int rotation) { auto texCoords = std::array, 4> { { { { 0.f, 1.f } }, { { 1.f, 1.f } }, @@ -182,30 +321,11 @@ void OverlayWidget::RendererGL::paintTransformedStaticContent( _contentBuffer->bind(); _contentBuffer->write(0, coords, sizeof(coords)); - _f->glUseProgram(_imageProgram->programId()); - _imageProgram->setUniformValue("viewport", QSizeF(_viewport * _factor)); - _imageProgram->setUniformValue("s_texture", GLint(0)); - - _f->glActiveTexture(GL_TEXTURE0); - _textures.bind(*_f, 0); - const auto cacheKey = image.cacheKey(); - const auto upload = (_cacheKey != cacheKey); - if (upload) { - const auto stride = image.bytesPerLine() / 4; - const auto data = image.constBits(); - uploadTexture( - GL_RGBA, - GL_RGBA, - image.size(), - _rgbaSize, - stride, - data); - _rgbaSize = image.size(); - _ySize = QSize(); - } + program->setUniformValue("viewport", QSizeF(_viewport * _factor)); + program->setUniformValue("s_texture", GLint(0)); toggleBlending(false); - FillTexturedRectangle(*_f, &*_imageProgram); + FillTexturedRectangle(*_f, &*program); } void OverlayWidget::RendererGL::uploadTexture( @@ -310,6 +430,23 @@ void OverlayWidget::RendererGL::paintGroupThumbs( }, kGroupThumbsOffset, true); } +void OverlayWidget::RendererGL::invalidate() { + _trackFrameIndex = -1; + _streamedIndex = -1; + const auto images = { + &_radialImage, + &_documentBubbleImage, + &_themePreviewImage, + &_saveMsgImage, + &_footerImage, + &_captionImage, + &_groupThumbsImage, + }; + for (const auto image : images) { + image->setImage(QImage()); + } +} + void OverlayWidget::RendererGL::paintUsingRaster( Ui::GL::Image &image, QRect rect, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h index 2400b3a52..d00679f74 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h @@ -49,6 +49,10 @@ private: QRect rect, int rotation, bool fillTransparentBackground) override; + void paintTransformedContent( + not_null program, + QRect rect, + int rotation); void paintRadialLoading( QRect inner, bool radial, @@ -67,6 +71,8 @@ private: void paintCaption(QRect outer, float64 opacity) override; void paintGroupThumbs(QRect outer, float64 opacity) override; + void invalidate() override; + void paintUsingRaster( Ui::GL::Image &image, QRect rect, @@ -97,11 +103,16 @@ private: std::optional _contentBuffer; std::optional _imageProgram; - + QOpenGLShader *_texturedVertexShader = nullptr; + std::optional _withTransparencyProgram; + std::optional _yuv420Program; Ui::GL::Textures<3> _textures; QSize _rgbaSize; - QSize _ySize; + QSize _lumaSize; + QSize _chromaSize; quint64 _cacheKey = 0; + int _trackFrameIndex = 0; + int _streamedIndex = 0; Ui::GL::Image _radialImage; Ui::GL::Image _documentBubbleImage; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp index 9d0bc291f..70d9d8d95 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp @@ -165,4 +165,7 @@ void OverlayWidget::RendererSW::paintGroupThumbs( } } +void OverlayWidget::RendererSW::invalidate() { +} + } // namespace Media::View diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.h b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h index 1b9ecd9d0..4ca8d0dfc 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h @@ -51,6 +51,8 @@ private: void paintCaption(QRect outer, float64 opacity) override; void paintGroupThumbs(QRect outer, float64 opacity) override; + void invalidate() override; + const not_null _owner; QBrush _transparentBrush; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h index 189e65445..e10f12253 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h @@ -38,6 +38,8 @@ public: virtual void paintCaption(QRect outer, float64 opacity) = 0; virtual void paintGroupThumbs(QRect outer, float64 opacity) = 0; + virtual void invalidate() = 0; + }; } // namespace Media::View diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index f10ae26f9..1d67bfdaf 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -555,6 +555,22 @@ QImage OverlayWidget::videoFrame() const { : _streamed->instance.info().video.cover; } +Streaming::FrameWithInfo OverlayWidget::videoFrameWithInfo() const { + Expects(videoShown()); + + return _streamed->instance.player().ready() + ? _streamed->instance.frameWithInfo() + : Streaming::FrameWithInfo{ + .original = _streamed->instance.info().video.cover, + .format = Streaming::FrameFormat::ARGB32, + .index = -2, + }; +} + +int OverlayWidget::streamedIndex() const { + return _streamedCreated; +} + bool OverlayWidget::documentContentShown() const { return _document && (!_staticContent.isNull() || videoShown()); } @@ -2618,6 +2634,7 @@ bool OverlayWidget::createStreamingObjects() { _streamed = nullptr; return false; } + ++_streamedCreated; _streamed->instance.setPriority(kOverlayLoaderPriority); _streamed->instance.lockPlayer(); _streamed->withSound = _document @@ -4429,8 +4446,10 @@ void OverlayWidget::clearBeforeHide() { _controlsOpacity = anim::value(1, 1); _groupThumbs = nullptr; _groupThumbsRect = QRect(); - if (_streamed) { - _streamed->controls.hide(); + for (const auto child : _widget->children()) { + if (child->isWidgetType()) { + static_cast(child)->hide(); + } } } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index f8e7056ec..acb7a67f0 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -47,6 +47,7 @@ struct TrackState; namespace Streaming { struct Information; struct Update; +struct FrameWithInfo; enum class Error; } // namespace Streaming } // namespace Media @@ -397,6 +398,8 @@ private: [[nodiscard]] QSize videoSize() const; [[nodiscard]] bool videoIsGifOrUserpic() const; [[nodiscard]] QImage videoFrame() const; + [[nodiscard]] Streaming::FrameWithInfo videoFrameWithInfo() const; + [[nodiscard]] int streamedIndex() const; [[nodiscard]] QImage transformedShownContent() const; [[nodiscard]] QImage transformShownContent( QImage content, @@ -474,6 +477,7 @@ private: std::unique_ptr _streamed; std::unique_ptr _pip; + int _streamedCreated = 0; bool _showAsPip = false; const style::icon *_docIcon = nullptr;