Play webm stickers only once if looping is disabled.
This commit is contained in:
parent
b4a49de819
commit
95e806cb89
|
@ -415,9 +415,21 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
||||||
activeOwnPlaying->frozenFrame = QImage();
|
activeOwnPlaying->frozenFrame = QImage();
|
||||||
activeOwnPlaying->frozenStatusText = QString();
|
activeOwnPlaying->frozenStatusText = QString();
|
||||||
}
|
}
|
||||||
p.drawImage(rthumb, streamed->frame(request));
|
|
||||||
if (!paused) {
|
const auto frame = streamed->frameWithInfo(request);
|
||||||
streamed->markFrameShown();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,10 @@ public:
|
||||||
QPoint resolveCustomInfoRightBottom() const override;
|
QPoint resolveCustomInfoRightBottom() const override;
|
||||||
QString additionalInfoString() const override;
|
QString additionalInfoString() const override;
|
||||||
|
|
||||||
|
void stickerClearLoopPlayed() override {
|
||||||
|
_stickerOncePlayed = false;
|
||||||
|
}
|
||||||
|
|
||||||
bool skipBubbleTail() const override {
|
bool skipBubbleTail() const override {
|
||||||
return isRoundedInBubbleBottom() && _caption.isEmpty();
|
return isRoundedInBubbleBottom() && _caption.isEmpty();
|
||||||
}
|
}
|
||||||
|
@ -192,6 +196,7 @@ private:
|
||||||
ClickHandlerPtr _stickerLink;
|
ClickHandlerPtr _stickerLink;
|
||||||
|
|
||||||
QString _downloadSize;
|
QString _downloadSize;
|
||||||
|
mutable bool _stickerOncePlayed = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ struct FrameYUV420 {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FrameWithInfo {
|
struct FrameWithInfo {
|
||||||
QImage original;
|
QImage image;
|
||||||
FrameYUV420 *yuv420 = nullptr;
|
FrameYUV420 *yuv420 = nullptr;
|
||||||
FrameFormat format = FrameFormat::None;
|
FrameFormat format = FrameFormat::None;
|
||||||
int index = -1;
|
int index = -1;
|
||||||
|
|
|
@ -172,6 +172,10 @@ QImage Instance::frame(const FrameRequest &request) const {
|
||||||
return player().frame(request, this);
|
return player().frame(request, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FrameWithInfo Instance::frameWithInfo(const FrameRequest &request) const {
|
||||||
|
return player().frameWithInfo(request, this);
|
||||||
|
}
|
||||||
|
|
||||||
FrameWithInfo Instance::frameWithInfo() const {
|
FrameWithInfo Instance::frameWithInfo() const {
|
||||||
return player().frameWithInfo(this);
|
return player().frameWithInfo(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,8 @@ public:
|
||||||
void callWaitingCallback();
|
void callWaitingCallback();
|
||||||
|
|
||||||
[[nodiscard]] QImage frame(const FrameRequest &request) const;
|
[[nodiscard]] QImage frame(const FrameRequest &request) const;
|
||||||
|
[[nodiscard]] FrameWithInfo frameWithInfo(
|
||||||
|
const FrameRequest &request) const;
|
||||||
[[nodiscard]] FrameWithInfo frameWithInfo() const;
|
[[nodiscard]] FrameWithInfo frameWithInfo() const;
|
||||||
bool markFrameShown() const;
|
bool markFrameShown() const;
|
||||||
|
|
||||||
|
|
|
@ -884,6 +884,14 @@ QImage Player::frame(
|
||||||
return _video->frame(request, instance);
|
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 {
|
FrameWithInfo Player::frameWithInfo(const Instance *instance) const {
|
||||||
Expects(_video != nullptr);
|
Expects(_video != nullptr);
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,9 @@ public:
|
||||||
[[nodiscard]] QImage frame(
|
[[nodiscard]] QImage frame(
|
||||||
const FrameRequest &request,
|
const FrameRequest &request,
|
||||||
const Instance *instance = nullptr) const;
|
const Instance *instance = nullptr) const;
|
||||||
|
[[nodiscard]] FrameWithInfo frameWithInfo(
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance = nullptr) const;
|
||||||
[[nodiscard]] FrameWithInfo frameWithInfo(
|
[[nodiscard]] FrameWithInfo frameWithInfo(
|
||||||
const Instance *instance = nullptr) const; // !requireARGB32
|
const Instance *instance = nullptr) const; // !requireARGB32
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,7 @@ private:
|
||||||
Fn<void(Error)> _error;
|
Fn<void(Error)> _error;
|
||||||
crl::time _pausedTime = kTimeUnknown;
|
crl::time _pausedTime = kTimeUnknown;
|
||||||
crl::time _resumedTime = kTimeUnknown;
|
crl::time _resumedTime = kTimeUnknown;
|
||||||
|
int _frameIndex = 0;
|
||||||
int _durationByLastPacket = 0;
|
int _durationByLastPacket = 0;
|
||||||
mutable TimePoint _syncTimePoint;
|
mutable TimePoint _syncTimePoint;
|
||||||
crl::time _loopingShift = 0;
|
crl::time _loopingShift = 0;
|
||||||
|
@ -330,6 +331,7 @@ bool VideoTrackObject::loopAround() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
avcodec_flush_buffers(_stream.codec.get());
|
avcodec_flush_buffers(_stream.codec.get());
|
||||||
|
_frameIndex = 0;
|
||||||
_loopingShift += duration;
|
_loopingShift += duration;
|
||||||
_readTillEnd = false;
|
_readTillEnd = false;
|
||||||
return true;
|
return true;
|
||||||
|
@ -372,6 +374,7 @@ auto VideoTrackObject::readFrame(not_null<Frame*> frame) -> FrameResult {
|
||||||
return FrameResult::Error;
|
return FrameResult::Error;
|
||||||
}
|
}
|
||||||
std::swap(frame->decoded, _stream.frame);
|
std::swap(frame->decoded, _stream.frame);
|
||||||
|
frame->index = _frameIndex++;
|
||||||
frame->position = position;
|
frame->position = position;
|
||||||
frame->displayed = kTimeUnknown;
|
frame->displayed = kTimeUnknown;
|
||||||
return FrameResult::Done;
|
return FrameResult::Done;
|
||||||
|
@ -651,6 +654,7 @@ void VideoTrackObject::callReady() {
|
||||||
Expects(_ready != nullptr);
|
Expects(_ready != nullptr);
|
||||||
|
|
||||||
const auto frame = _shared->frameForPaint();
|
const auto frame = _shared->frameForPaint();
|
||||||
|
++_frameIndex;
|
||||||
|
|
||||||
auto data = VideoInformation();
|
auto data = VideoInformation();
|
||||||
data.size = FFmpeg::CorrectByAspect(
|
data.size = FFmpeg::CorrectByAspect(
|
||||||
|
@ -961,9 +965,6 @@ bool VideoTrack::Shared::markFrameShown() {
|
||||||
if (frame->displayed == kTimeUnknown) {
|
if (frame->displayed == kTimeUnknown) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (counter == 2 * kFramesCount - 1) {
|
|
||||||
++_counterCycle;
|
|
||||||
}
|
|
||||||
_counter.store(
|
_counter.store(
|
||||||
next,
|
next,
|
||||||
std::memory_order_release);
|
std::memory_order_release);
|
||||||
|
@ -995,7 +996,7 @@ VideoTrack::FrameWithIndex VideoTrack::Shared::frameForPaintWithIndex() {
|
||||||
Assert(frame->displayed != kTimeUnknown);
|
Assert(frame->displayed != kTimeUnknown);
|
||||||
return {
|
return {
|
||||||
.frame = frame,
|
.frame = frame,
|
||||||
.index = (_counterCycle * 2 * kFramesCount) + index,
|
.index = frame->index,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1101,7 +1102,44 @@ bool VideoTrack::markFrameShown() {
|
||||||
QImage VideoTrack::frame(
|
QImage VideoTrack::frame(
|
||||||
const FrameRequest &request,
|
const FrameRequest &request,
|
||||||
const Instance *instance) {
|
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*> frame,
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance) {
|
||||||
const auto i = frame->prepared.find(instance);
|
const auto i = frame->prepared.find(instance);
|
||||||
const auto none = (i == frame->prepared.end());
|
const auto none = (i == frame->prepared.end());
|
||||||
const auto preparedFor = frame->prepared.empty()
|
const auto preparedFor = frame->prepared.empty()
|
||||||
|
@ -1151,26 +1189,6 @@ QImage VideoTrack::frame(
|
||||||
return i->second.image;
|
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() {
|
QImage VideoTrack::currentFrameImage() {
|
||||||
const auto frame = _shared->frameForPaint();
|
const auto frame = _shared->frameForPaint();
|
||||||
if (frame->original.isNull() && frame->format == FrameFormat::YUV420) {
|
if (frame->original.isNull() && frame->format == FrameFormat::YUV420) {
|
||||||
|
|
|
@ -58,6 +58,9 @@ public:
|
||||||
[[nodiscard]] QImage frame(
|
[[nodiscard]] QImage frame(
|
||||||
const FrameRequest &request,
|
const FrameRequest &request,
|
||||||
const Instance *instance);
|
const Instance *instance);
|
||||||
|
[[nodiscard]] FrameWithInfo frameWithInfo(
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance);
|
||||||
[[nodiscard]] FrameWithInfo frameWithInfo(const Instance *instance);
|
[[nodiscard]] FrameWithInfo frameWithInfo(const Instance *instance);
|
||||||
[[nodiscard]] QImage currentFrameImage();
|
[[nodiscard]] QImage currentFrameImage();
|
||||||
void unregisterInstance(not_null<const Instance*> instance);
|
void unregisterInstance(not_null<const Instance*> instance);
|
||||||
|
@ -88,6 +91,7 @@ private:
|
||||||
|
|
||||||
base::flat_map<const Instance*, Prepared> prepared;
|
base::flat_map<const Instance*, Prepared> prepared;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
bool alpha = false;
|
bool alpha = false;
|
||||||
};
|
};
|
||||||
struct FrameWithIndex {
|
struct FrameWithIndex {
|
||||||
|
@ -141,9 +145,6 @@ private:
|
||||||
static constexpr auto kCounterUninitialized = -1;
|
static constexpr auto kCounterUninitialized = -1;
|
||||||
std::atomic<int> _counter = kCounterUninitialized;
|
std::atomic<int> _counter = kCounterUninitialized;
|
||||||
|
|
||||||
// Main thread.
|
|
||||||
int _counterCycle = 0;
|
|
||||||
|
|
||||||
static constexpr auto kFramesCount = 4;
|
static constexpr auto kFramesCount = 4;
|
||||||
std::array<Frame, kFramesCount> _frames;
|
std::array<Frame, kFramesCount> _frames;
|
||||||
|
|
||||||
|
@ -160,6 +161,11 @@ private:
|
||||||
not_null<const Frame*> frame,
|
not_null<const Frame*> frame,
|
||||||
crl::time trackTime);
|
crl::time trackTime);
|
||||||
|
|
||||||
|
[[nodiscard]] QImage frameImage(
|
||||||
|
not_null<Frame*> frame,
|
||||||
|
const FrameRequest &request,
|
||||||
|
const Instance *instance);
|
||||||
|
|
||||||
const int _streamIndex = 0;
|
const int _streamIndex = 0;
|
||||||
const AVRational _streamTimeBase;
|
const AVRational _streamTimeBase;
|
||||||
const crl::time _streamDuration = 0;
|
const crl::time _streamDuration = 0;
|
||||||
|
|
|
@ -188,9 +188,9 @@ void OverlayWidget::RendererGL::paintTransformedVideoFrame(
|
||||||
if (data.format == Streaming::FrameFormat::None) {
|
if (data.format == Streaming::FrameFormat::None) {
|
||||||
return;
|
return;
|
||||||
} else if (data.format == Streaming::FrameFormat::ARGB32) {
|
} else if (data.format == Streaming::FrameFormat::ARGB32) {
|
||||||
Assert(!data.original.isNull());
|
Assert(!data.image.isNull());
|
||||||
paintTransformedStaticContent(
|
paintTransformedStaticContent(
|
||||||
data.original,
|
data.image,
|
||||||
geometry,
|
geometry,
|
||||||
data.alpha,
|
data.alpha,
|
||||||
data.alpha);
|
data.alpha);
|
||||||
|
|
|
@ -610,7 +610,7 @@ Streaming::FrameWithInfo OverlayWidget::videoFrameWithInfo() const {
|
||||||
return _streamed->instance.player().ready()
|
return _streamed->instance.player().ready()
|
||||||
? _streamed->instance.frameWithInfo()
|
? _streamed->instance.frameWithInfo()
|
||||||
: Streaming::FrameWithInfo{
|
: Streaming::FrameWithInfo{
|
||||||
.original = _streamed->instance.info().video.cover,
|
.image = _streamed->instance.info().video.cover,
|
||||||
.format = Streaming::FrameFormat::ARGB32,
|
.format = Streaming::FrameFormat::ARGB32,
|
||||||
.index = -2,
|
.index = -2,
|
||||||
.alpha = _streamed->instance.info().video.alpha,
|
.alpha = _streamed->instance.info().video.alpha,
|
||||||
|
|
|
@ -285,8 +285,8 @@ void Pip::RendererGL::paintTransformedVideoFrame(
|
||||||
}
|
}
|
||||||
geometry.rotation = (geometry.rotation + geometry.videoRotation) % 360;
|
geometry.rotation = (geometry.rotation + geometry.videoRotation) % 360;
|
||||||
if (data.format == Streaming::FrameFormat::ARGB32) {
|
if (data.format == Streaming::FrameFormat::ARGB32) {
|
||||||
Assert(!data.original.isNull());
|
Assert(!data.image.isNull());
|
||||||
paintTransformedStaticContent(data.original, geometry);
|
paintTransformedStaticContent(data.image, geometry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Assert(data.format == Streaming::FrameFormat::YUV420);
|
Assert(data.format == Streaming::FrameFormat::YUV420);
|
||||||
|
|
Loading…
Reference in New Issue