mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Support rendering Webm videos with alpha.
This commit is contained in:
parent
1755ead681
commit
8b7d2c880e
25 changed files with 139 additions and 44 deletions
|
@ -858,8 +858,8 @@ void StickerSetBox::Inner::paintSticker(
|
|||
const auto &media = element.documentMedia;
|
||||
media->checkStickerSmall();
|
||||
|
||||
const auto isAnimated = document->sticker()->animated;
|
||||
if (isAnimated
|
||||
const auto isLottie = document->sticker()->isLottie();
|
||||
if (isLottie
|
||||
&& !element.animated
|
||||
&& media->loaded()) {
|
||||
const_cast<Inner*>(this)->setupLottie(index);
|
||||
|
@ -867,7 +867,7 @@ void StickerSetBox::Inner::paintSticker(
|
|||
|
||||
auto w = 1;
|
||||
auto h = 1;
|
||||
if (isAnimated && !document->dimensions.isEmpty()) {
|
||||
if (isLottie && !document->dimensions.isEmpty()) {
|
||||
const auto request = Lottie::FrameRequest{ boundingBoxSize() * cIntRetinaFactor() };
|
||||
const auto size = request.size(document->dimensions, true) / cIntRetinaFactor();
|
||||
w = std::max(size.width(), 1);
|
||||
|
|
|
@ -827,7 +827,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
|
|||
const auto &media = sticker.documentMedia;
|
||||
if (!document->sticker()) continue;
|
||||
|
||||
if (document->sticker()->animated
|
||||
if (document->sticker()->isLottie()
|
||||
&& !sticker.animated
|
||||
&& media->loaded()) {
|
||||
setupLottie(sticker);
|
||||
|
|
|
@ -141,7 +141,7 @@ void DicePack::generateLocal(int index, const QString &name) {
|
|||
_map.emplace(index, document);
|
||||
|
||||
Ensures(document->sticker());
|
||||
Ensures(document->sticker()->animated);
|
||||
Ensures(document->sticker()->isLottie());
|
||||
}
|
||||
|
||||
DicePacks::DicePacks(not_null<Main::Session*> session) : _session(session) {
|
||||
|
|
|
@ -1916,8 +1916,8 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section,
|
|||
return;
|
||||
}
|
||||
|
||||
const auto isAnimated = document->sticker()->animated;
|
||||
if (isAnimated
|
||||
const auto isLottie = document->sticker()->isLottie();
|
||||
if (isLottie
|
||||
&& !sticker.animated
|
||||
&& media->loaded()) {
|
||||
setupLottie(set, section, index);
|
||||
|
@ -1936,7 +1936,7 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section,
|
|||
|
||||
auto w = 1;
|
||||
auto h = 1;
|
||||
if (isAnimated && !document->dimensions.isEmpty()) {
|
||||
if (isLottie && !document->dimensions.isEmpty()) {
|
||||
const auto request = Lottie::FrameRequest{ boundingBoxSize() * cIntRetinaFactor() };
|
||||
const auto size = request.size(document->dimensions, true) / cIntRetinaFactor();
|
||||
w = std::max(size.width(), 1);
|
||||
|
|
|
@ -139,7 +139,7 @@ bool HasLottieThumbnail(
|
|||
}
|
||||
const auto document = media->owner();
|
||||
if (const auto info = document->sticker()) {
|
||||
if (!info->animated) {
|
||||
if (!info->isLottie()) {
|
||||
return false;
|
||||
}
|
||||
media->automaticLoad(document->stickerSetOrigin(), nullptr);
|
||||
|
|
|
@ -109,6 +109,14 @@ MimeType MimeTypeForData(const QByteArray &data) {
|
|||
return MimeType(QMimeDatabase().mimeTypeForData(data));
|
||||
}
|
||||
|
||||
bool IsMimeStickerLottie(const QString &mime) {
|
||||
return (mime == u"application/x-tgsticker"_q);
|
||||
}
|
||||
|
||||
bool IsMimeStickerWebm(const QString &mime) {
|
||||
return (mime == u"video/webm"_q);
|
||||
}
|
||||
|
||||
bool IsMimeStickerAnimated(const QString &mime) {
|
||||
return (mime == u"application/x-tgsticker"_q);
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace {
|
||||
|
||||
const auto kAnimatedStickerDimensions = QSize(
|
||||
const auto kLottieStickerDimensions = QSize(
|
||||
kStickerSideSize,
|
||||
kStickerSideSize);
|
||||
|
||||
|
@ -262,6 +262,22 @@ Data::FileOrigin StickerData::setOrigin() const {
|
|||
: Data::FileOrigin();
|
||||
}
|
||||
|
||||
bool StickerData::isStatic() const {
|
||||
return (type == StickerType::Webp);
|
||||
}
|
||||
|
||||
bool StickerData::isLottie() const {
|
||||
return (type == StickerType::Tgs);
|
||||
}
|
||||
|
||||
bool StickerData::isAnimated() const {
|
||||
return !isStatic();
|
||||
}
|
||||
|
||||
bool StickerData::isWebm() const {
|
||||
return (type == StickerType::Webm);
|
||||
}
|
||||
|
||||
VoiceData::~VoiceData() {
|
||||
if (!waveform.isEmpty()
|
||||
&& waveform[0] == -1
|
||||
|
@ -380,12 +396,19 @@ void DocumentData::setattributes(
|
|||
}
|
||||
if (type == StickerDocument
|
||||
&& ((size > Storage::kMaxStickerBytesSize)
|
||||
|| (!sticker()->animated
|
||||
|| (!sticker()->isLottie()
|
||||
&& !GoodStickerDimensions(
|
||||
dimensions.width(),
|
||||
dimensions.height())))) {
|
||||
type = FileDocument;
|
||||
_additional = nullptr;
|
||||
} else if (type == FileDocument
|
||||
&& hasMimeType(qstr("video/webm"))
|
||||
&& (size < Storage::kMaxStickerBytesSize)
|
||||
&& GoodStickerDimensions(dimensions.width(), dimensions.height())) {
|
||||
type = StickerDocument;
|
||||
_additional = std::make_unique<StickerData>();
|
||||
sticker()->type = StickerType::Webm;
|
||||
}
|
||||
if (isAudioFile() || isAnimation() || isVoiceMessage()) {
|
||||
setMaybeSupportsStreaming(true);
|
||||
|
@ -397,8 +420,8 @@ void DocumentData::validateLottieSticker() {
|
|||
&& hasMimeType(qstr("application/x-tgsticker"))) {
|
||||
type = StickerDocument;
|
||||
_additional = std::make_unique<StickerData>();
|
||||
sticker()->animated = true;
|
||||
dimensions = kAnimatedStickerDimensions;
|
||||
sticker()->type = StickerType::Tgs;
|
||||
dimensions = kLottieStickerDimensions;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,12 +57,22 @@ struct DocumentAdditionalData {
|
|||
|
||||
};
|
||||
|
||||
struct StickerData : public DocumentAdditionalData {
|
||||
Data::FileOrigin setOrigin() const;
|
||||
enum class StickerType : uchar {
|
||||
Webp,
|
||||
Tgs,
|
||||
Webm,
|
||||
};
|
||||
|
||||
struct StickerData : public DocumentAdditionalData {
|
||||
[[nodiscard]] Data::FileOrigin setOrigin() const;
|
||||
[[nodiscard]] bool isStatic() const;
|
||||
[[nodiscard]] bool isLottie() const;
|
||||
[[nodiscard]] bool isAnimated() const;
|
||||
[[nodiscard]] bool isWebm() const;
|
||||
|
||||
bool animated = false;
|
||||
QString alt;
|
||||
StickerSetIdentifier set;
|
||||
StickerType type = StickerType::Webp;
|
||||
};
|
||||
|
||||
struct SongData : public DocumentAdditionalData {
|
||||
|
|
|
@ -49,7 +49,7 @@ enum class FileType {
|
|||
|| owner->isAnimation()
|
||||
|| owner->isWallPaper()
|
||||
|| owner->isTheme()
|
||||
|| (owner->sticker() && owner->sticker()->animated);
|
||||
|| (owner->sticker() && owner->sticker()->isAnimated());
|
||||
}
|
||||
|
||||
[[nodiscard]] QImage PrepareGoodThumbnail(
|
||||
|
@ -260,7 +260,7 @@ void DocumentMedia::checkStickerLarge() {
|
|||
return;
|
||||
}
|
||||
automaticLoad(_owner->stickerSetOrigin(), nullptr);
|
||||
if (data->animated || !loaded()) {
|
||||
if (data->isAnimated() || !loaded()) {
|
||||
return;
|
||||
}
|
||||
if (_bytes.isEmpty()) {
|
||||
|
@ -366,9 +366,9 @@ bool DocumentMedia::thumbnailEnoughForSticker() const {
|
|||
|
||||
void DocumentMedia::checkStickerSmall() {
|
||||
const auto data = _owner->sticker();
|
||||
if ((data && data->animated) || thumbnailEnoughForSticker()) {
|
||||
if ((data && data->isAnimated()) || thumbnailEnoughForSticker()) {
|
||||
_owner->loadThumbnail(_owner->stickerSetOrigin());
|
||||
if (data && data->animated) {
|
||||
if (data && data->isAnimated()) {
|
||||
automaticLoad(_owner->stickerSetOrigin(), nullptr);
|
||||
}
|
||||
} else {
|
||||
|
@ -383,7 +383,7 @@ Image *DocumentMedia::getStickerLarge() {
|
|||
|
||||
Image *DocumentMedia::getStickerSmall() {
|
||||
const auto data = _owner->sticker();
|
||||
if ((data && data->animated) || thumbnailEnoughForSticker()) {
|
||||
if ((data && data->isAnimated()) || thumbnailEnoughForSticker()) {
|
||||
return thumbnail();
|
||||
}
|
||||
return _sticker.get();
|
||||
|
|
|
@ -994,7 +994,7 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
|
|||
const auto CreateSortKey = [&](
|
||||
not_null<DocumentData*> document,
|
||||
int base) {
|
||||
if (document->sticker() && document->sticker()->animated) {
|
||||
if (document->sticker() && document->sticker()->isAnimated()) {
|
||||
base += kSlice;
|
||||
}
|
||||
return TimeId(base + int((document->id ^ seed) % kSlice));
|
||||
|
@ -1005,7 +1005,7 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
|
|||
auto myCounter = 0;
|
||||
const auto CreateMySortKey = [&](not_null<DocumentData*> document) {
|
||||
auto base = kSlice * 6;
|
||||
if (!document->sticker() || !document->sticker()->animated) {
|
||||
if (!document->sticker() || !document->sticker()->isAnimated()) {
|
||||
base -= kSlice;
|
||||
}
|
||||
return (base - (++myCounter));
|
||||
|
@ -1019,7 +1019,7 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
|
|||
const auto InstallDateAdjusted = [&](
|
||||
TimeId date,
|
||||
not_null<DocumentData*> document) {
|
||||
return (document->sticker() && document->sticker()->animated)
|
||||
return (document->sticker() && document->sticker()->isAnimated())
|
||||
? date
|
||||
: date / 2;
|
||||
};
|
||||
|
|
|
@ -38,7 +38,7 @@ ItemSticker::ItemSticker(
|
|||
? 1.0
|
||||
: (_pixmap.height() / float64(_pixmap.width())));
|
||||
});
|
||||
if (stickerData->animated) {
|
||||
if (stickerData->isLottie()) {
|
||||
_lottie.player = ChatHelpers::LottiePlayerFromDocument(
|
||||
_mediaView.get(),
|
||||
ChatHelpers::StickerLottieSize::MessageHistory,
|
||||
|
|
|
@ -391,6 +391,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
auto request = ::Media::Streaming::FrameRequest();
|
||||
request.outer = QSize(usew, painth) * cIntRetinaFactor();
|
||||
request.resize = QSize(_thumbw, _thumbh) * cIntRetinaFactor();
|
||||
request.keepAlpha = true; AssertIsDebug();
|
||||
request.corners = roundCorners;
|
||||
request.radius = roundRadius;
|
||||
if (!activeRoundPlaying && activeOwnPlaying->instance.playerLocked()) {
|
||||
|
@ -992,6 +993,7 @@ void Gif::drawGrouped(
|
|||
{ geometry.width(), geometry.height() });
|
||||
request.outer = geometry.size() * cIntRetinaFactor();
|
||||
request.resize = pixSize * cIntRetinaFactor();
|
||||
request.keepAlpha = true; AssertIsDebug();
|
||||
request.corners = corners;
|
||||
request.radius = roundRadius;
|
||||
if (activeOwnPlaying->instance.playerLocked()) {
|
||||
|
|
|
@ -123,7 +123,7 @@ bool Sticker::readyToDrawLottie() {
|
|||
ensureDataMediaCreated();
|
||||
_dataMedia->checkStickerLarge();
|
||||
const auto loaded = _dataMedia->loaded();
|
||||
if (sticker->animated && !_lottie && loaded) {
|
||||
if (sticker->isLottie() && !_lottie && loaded) {
|
||||
setupLottie();
|
||||
}
|
||||
return (_lottie && _lottie->ready());
|
||||
|
@ -147,7 +147,7 @@ void Sticker::draw(
|
|||
if (readyToDrawLottie()) {
|
||||
paintLottie(p, context, r);
|
||||
} else if (!_data->sticker()
|
||||
|| (_data->sticker()->animated && _replacements)
|
||||
|| (_data->sticker()->isLottie() && _replacements)
|
||||
|| !paintPixmap(p, context, r)) {
|
||||
paintPath(p, context, r);
|
||||
}
|
||||
|
|
|
@ -554,7 +554,7 @@ void Sticker::prepareThumbnail() const {
|
|||
ensureDataMediaCreated(document);
|
||||
if (!_lottie
|
||||
&& document->sticker()
|
||||
&& document->sticker()->animated
|
||||
&& document->sticker()->isLottie()
|
||||
&& _dataMedia->loaded()) {
|
||||
setupLottie();
|
||||
}
|
||||
|
|
|
@ -188,8 +188,16 @@ crl::time FFMpegReaderImplementation::framePresentationTime() const {
|
|||
}
|
||||
|
||||
crl::time FFMpegReaderImplementation::durationMs() const {
|
||||
if (_fmtContext->streams[_streamId]->duration == AV_NOPTS_VALUE) return 0;
|
||||
return (_fmtContext->streams[_streamId]->duration * 1000LL * _fmtContext->streams[_streamId]->time_base.num) / _fmtContext->streams[_streamId]->time_base.den;
|
||||
const auto rebase = [](int64_t duration, const AVRational &base) {
|
||||
return (duration * 1000LL * base.num) / base.den;
|
||||
};
|
||||
const auto stream = _fmtContext->streams[_streamId];
|
||||
if (stream->duration != AV_NOPTS_VALUE) {
|
||||
return rebase(stream->duration, stream->time_base);
|
||||
} else if (_fmtContext->duration != AV_NOPTS_VALUE) {
|
||||
return rebase(_fmtContext->duration, AVRational{ 1, AV_TIME_BASE });
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const QSize &size) {
|
||||
|
@ -211,8 +219,12 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
|
|||
if (to.isNull() || to.size() != toSize || !to.isDetached() || !isAlignedImage(to)) {
|
||||
to = createAlignedImage(toSize);
|
||||
}
|
||||
hasAlpha = (_frame->format == AV_PIX_FMT_BGRA || (_frame->format == -1 && _codecContext->pix_fmt == AV_PIX_FMT_BGRA));
|
||||
if (_frame->width == toSize.width() && _frame->height == toSize.height() && hasAlpha) {
|
||||
const auto format = (_frame->format == AV_PIX_FMT_NONE)
|
||||
? _codecContext->pix_fmt
|
||||
: _frame->format;
|
||||
const auto bgra = (format == AV_PIX_FMT_BGRA);
|
||||
hasAlpha = bgra || (format == AV_PIX_FMT_YUVA420P);
|
||||
if (_frame->width == toSize.width() && _frame->height == toSize.height() && bgra) {
|
||||
int32 sbpl = _frame->linesize[0], dbpl = to.bytesPerLine(), bpl = qMin(sbpl, dbpl);
|
||||
uchar *s = _frame->data[0], *d = to.bits();
|
||||
for (int32 i = 0, l = _frame->height; i < l; ++i) {
|
||||
|
@ -228,7 +240,7 @@ bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const Q
|
|||
int toLinesize[AV_NUM_DATA_POINTERS] = { int(to.bytesPerLine()), 0 };
|
||||
sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _frame->height, toData, toLinesize);
|
||||
}
|
||||
if (hasAlpha) {
|
||||
if (bgra) {
|
||||
FFmpeg::PremultiplyInplace(to);
|
||||
}
|
||||
if (_rotation != Rotation::None) {
|
||||
|
@ -391,6 +403,19 @@ bool FFMpegReaderImplementation::isGifv() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool FFMpegReaderImplementation::isWebmSticker() const {
|
||||
if (_hasAudioStream) {
|
||||
return false;
|
||||
}
|
||||
if (dataSize() > kMaxInMemory) {
|
||||
return false;
|
||||
}
|
||||
if (_codecContext->codec_id != AV_CODEC_ID_VP9) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString FFMpegReaderImplementation::logData() const {
|
||||
return u"for file '%1', data size '%2'"_q.arg(_location ? _location->name() : QString()).arg(_data->size());
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
QString logData() const;
|
||||
|
||||
bool isGifv() const;
|
||||
bool isWebmSticker() const;
|
||||
|
||||
~FFMpegReaderImplementation();
|
||||
|
||||
|
|
|
@ -845,6 +845,7 @@ Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const
|
|||
auto durationMs = reader->durationMs();
|
||||
if (durationMs > 0) {
|
||||
result.isGifv = reader->isGifv();
|
||||
result.isWebmSticker = reader->isWebmSticker();
|
||||
// Use first video frame as a thumbnail.
|
||||
// All other apps and server do that way.
|
||||
//if (!result.isGifv) {
|
||||
|
@ -857,7 +858,7 @@ Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const
|
|||
auto readResult = reader->readFramesTill(-1, crl::now());
|
||||
auto readFrame = (readResult == internal::ReaderImplementation::ReadResult::Success);
|
||||
if (readFrame && reader->renderFrame(result.thumbnail, hasAlpha, QSize())) {
|
||||
if (hasAlpha) {
|
||||
if (hasAlpha && !result.isWebmSticker) {
|
||||
auto cacheForResize = QImage();
|
||||
auto request = FrameRequest();
|
||||
request.framew = request.outerw = result.thumbnail.width();
|
||||
|
|
|
@ -121,6 +121,7 @@ struct FrameRequest {
|
|||
ImageRoundRadius radius = ImageRoundRadius();
|
||||
RectParts corners = RectPart::AllCorners;
|
||||
bool requireARGB32 = true;
|
||||
bool keepAlpha = false;
|
||||
bool strict = true;
|
||||
|
||||
static FrameRequest NonStrict() {
|
||||
|
@ -138,6 +139,7 @@ struct FrameRequest {
|
|||
&& (outer == other.outer)
|
||||
&& (radius == other.radius)
|
||||
&& (corners == other.corners)
|
||||
&& (keepAlpha == other.keepAlpha)
|
||||
&& (requireARGB32 == other.requireARGB32);
|
||||
}
|
||||
[[nodiscard]] bool operator!=(const FrameRequest &other) const {
|
||||
|
@ -146,6 +148,7 @@ struct FrameRequest {
|
|||
|
||||
[[nodiscard]] bool goodFor(const FrameRequest &other) const {
|
||||
return (requireARGB32 == other.requireARGB32)
|
||||
&& (keepAlpha == other.keepAlpha)
|
||||
&& ((*this == other) || (strict && !other.strict));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -89,9 +89,10 @@ FFmpeg::AvErrorWrap ReadNextFrame(Stream &stream) {
|
|||
|
||||
bool GoodForRequest(
|
||||
const QImage &image,
|
||||
bool hasAlpha,
|
||||
int rotation,
|
||||
const FrameRequest &request) {
|
||||
if (image.isNull()) {
|
||||
if (image.isNull() || (hasAlpha && !request.keepAlpha)) {
|
||||
return false;
|
||||
} else if (request.resize.isEmpty()) {
|
||||
return true;
|
||||
|
@ -170,6 +171,10 @@ QImage ConvertFrame(
|
|||
frame->height,
|
||||
data,
|
||||
linesize);
|
||||
|
||||
if (frame->format == AV_PIX_FMT_YUVA420P) {
|
||||
FFmpeg::PremultiplyInplace(storage);
|
||||
}
|
||||
}
|
||||
|
||||
FFmpeg::ClearFrameMemory(frame);
|
||||
|
@ -274,8 +279,11 @@ void PaintFrameContent(
|
|||
(full.height() - size.height()) / 2,
|
||||
size.width(),
|
||||
size.height());
|
||||
PaintFrameOuter(p, to, full);
|
||||
PaintFrameInner(p, to, original, alpha, rotation);
|
||||
if (!alpha || !request.keepAlpha) {
|
||||
PaintFrameOuter(p, to, full);
|
||||
}
|
||||
const auto deAlpha = alpha && !request.keepAlpha;
|
||||
PaintFrameInner(p, to, original, deAlpha, rotation);
|
||||
}
|
||||
|
||||
void ApplyFrameRounding(QImage &storage, const FrameRequest &request) {
|
||||
|
@ -304,6 +312,10 @@ QImage PrepareByRequest(
|
|||
storage = FFmpeg::CreateFrameStorage(outer);
|
||||
}
|
||||
|
||||
if (alpha && request.keepAlpha) {
|
||||
storage.fill(Qt::transparent);
|
||||
}
|
||||
|
||||
QPainter p(&storage);
|
||||
PaintFrameContent(p, original, alpha, rotation, request);
|
||||
p.end();
|
||||
|
|
|
@ -51,6 +51,7 @@ struct Stream {
|
|||
|
||||
[[nodiscard]] bool GoodForRequest(
|
||||
const QImage &image,
|
||||
bool hasAlpha,
|
||||
int rotation,
|
||||
const FrameRequest &request);
|
||||
[[nodiscard]] QImage ConvertFrame(
|
||||
|
|
|
@ -444,7 +444,8 @@ void VideoTrackObject::rasterizeFrame(not_null<Frame*> frame) {
|
|||
}
|
||||
frame->format = FrameFormat::YUV420;
|
||||
} else {
|
||||
frame->alpha = (frame->decoded->format == AV_PIX_FMT_BGRA);
|
||||
frame->alpha = (frame->decoded->format == AV_PIX_FMT_BGRA)
|
||||
|| (frame->decoded->format == AV_PIX_FMT_YUVA420P);
|
||||
frame->yuv420.size = {
|
||||
frame->decoded->width,
|
||||
frame->decoded->height
|
||||
|
@ -1110,8 +1111,11 @@ QImage VideoTrack::frame(
|
|||
&& frame->format == FrameFormat::YUV420) {
|
||||
frame->original = ConvertToARGB32(frame->yuv420);
|
||||
}
|
||||
if (!frame->alpha
|
||||
&& GoodForRequest(frame->original, _streamRotation, useRequest)) {
|
||||
if (GoodForRequest(
|
||||
frame->original,
|
||||
frame->alpha,
|
||||
_streamRotation,
|
||||
useRequest)) {
|
||||
return frame->original;
|
||||
} else if (changed || none || i->second.image.isNull()) {
|
||||
const auto j = none
|
||||
|
@ -1187,8 +1191,11 @@ void VideoTrack::PrepareFrameByRequests(
|
|||
const auto end = frame->prepared.end();
|
||||
for (auto i = begin; i != end; ++i) {
|
||||
auto &prepared = i->second;
|
||||
if (frame->alpha
|
||||
|| !GoodForRequest(frame->original, rotation, prepared.request)) {
|
||||
if (!GoodForRequest(
|
||||
frame->original,
|
||||
frame->alpha,
|
||||
rotation,
|
||||
prepared.request)) {
|
||||
auto j = begin;
|
||||
for (; j != i; ++j) {
|
||||
if (j->second.request == prepared.request) {
|
||||
|
|
|
@ -619,6 +619,7 @@ bool FileLoadTask::CheckForVideo(
|
|||
static const auto extensions = {
|
||||
qstr(".mp4"),
|
||||
qstr(".mov"),
|
||||
qstr(".webm"),
|
||||
};
|
||||
if (!CheckMimeOrExtensions(filepath, result->filemime, mimes, extensions)) {
|
||||
return false;
|
||||
|
|
|
@ -298,7 +298,7 @@ void PrepareDetails(PreparedFile &file, int previewWidth) {
|
|||
UpdateImageDetails(file, previewWidth);
|
||||
file.type = PreparedFile::Type::Photo;
|
||||
} else if (Core::IsMimeSticker(file.information->filemime)
|
||||
|| image->animated) {
|
||||
|| image->animated) {
|
||||
file.type = PreparedFile::Type::None;
|
||||
}
|
||||
} else if (const auto video = std::get_if<Video>(
|
||||
|
|
|
@ -31,6 +31,7 @@ struct PreparedFileInformation {
|
|||
};
|
||||
struct Video {
|
||||
bool isGifv = false;
|
||||
bool isWebmSticker = false;
|
||||
bool supportsStreaming = false;
|
||||
int duration = -1;
|
||||
QImage thumbnail;
|
||||
|
|
|
@ -262,7 +262,7 @@ QPixmap MediaPreviewWidget::currentImage() const {
|
|||
if (_document) {
|
||||
if (const auto sticker = _document->sticker()) {
|
||||
if (_cacheStatus != CacheLoaded) {
|
||||
if (sticker->animated && !_lottie && _documentMedia->loaded()) {
|
||||
if (sticker->isLottie() && !_lottie && _documentMedia->loaded()) {
|
||||
const_cast<MediaPreviewWidget*>(this)->setupLottie();
|
||||
}
|
||||
if (_lottie && _lottie->ready()) {
|
||||
|
|
Loading…
Add table
Reference in a new issue