diff --git a/Telegram/SourceFiles/data/data_types.cpp b/Telegram/SourceFiles/data/data_types.cpp index 8fed3ecd30..ab60b8bfb6 100644 --- a/Telegram/SourceFiles/data/data_types.cpp +++ b/Telegram/SourceFiles/data/data_types.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_types.h" +#include "media/media_common.h" #include "ui/widgets/fields/input_field.h" #include "storage/cache/storage_cache_types.h" #include "base/openssl_help.h" @@ -157,8 +158,9 @@ BusinessShortcutId BusinessShortcutIdFromMessage( bool GoodStickerDimensions(int width, int height) { // Show all .webp (except very large ones) as stickers, // allow to open them in media viewer to see details. - constexpr auto kLargetsStickerSide = 2560; - return (width > 0) - && (height > 0) - && (width * height <= kLargetsStickerSide * kLargetsStickerSide); + constexpr auto kLargestStickerSide = 2560; + return ::Media::ValidFrameSize( + width, + height, + kLargestStickerSide * kLargestStickerSide); } diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp index 0f35c5ff23..13e3f7f865 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_frame_generator.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ffmpeg/ffmpeg_frame_generator.h" #include "ffmpeg/ffmpeg_utility.h" +#include "media/media_common.h" #include "base/debug_log.h" namespace FFmpeg { @@ -15,6 +16,8 @@ namespace { constexpr auto kMaxArea = 1920 * 1080 * 4; +using ::Media::ValidFrameSize; + } // namespace class FrameGenerator::Impl final { @@ -103,7 +106,11 @@ FrameGenerator::Impl::Impl(const QByteArray &bytes) const auto info = _format->streams[_streamId]; _rotation = ReadRotationFromMetadata(info); //_aspect = ValidateAspectRatio(info->sample_aspect_ratio); - _codec = MakeCodecPointer({ .stream = info }); + _codec = MakeCodecPointer({ + .stream = info, + .hwAllowed = false, + .videoMaxArea = kMaxArea, + }); } int FrameGenerator::Impl::Read(void *opaque, uint8_t *buf, int buf_size) { @@ -157,7 +164,7 @@ FrameGenerator::Frame FrameGenerator::Impl::renderCurrent( const auto width = frame->width; const auto height = frame->height; if (!width || !height) { - LOG(("Webm Error: Bad frame size: %1x%2 ").arg(width).arg(height)); + LOG(("Webm Error: Bad frame size %1x%2").arg(width).arg(height)); return {}; } @@ -167,6 +174,10 @@ FrameGenerator::Frame FrameGenerator::Impl::renderCurrent( } if (!GoodStorageForFrame(storage, size)) { storage = CreateFrameStorage(size); + if (storage.isNull()) { + LOG(("Webm Error: Bad frame size %1x%2").arg(width).arg(height)); + return {}; + } } const auto dx = (size.width() - scaled.width()) / 2; const auto dy = (size.height() - scaled.height()) / 2; @@ -317,7 +328,7 @@ void FrameGenerator::Impl::readNextFrame() { while (true) { auto result = avcodec_receive_frame(_codec.get(), frame.get()); if (result >= 0) { - if (frame->width * frame->height > kMaxArea) { + if (!ValidFrameSize(frame->width, frame->height, kMaxArea)) { return; } _next.frame = std::move(frame); diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp index 56e612358e..0c60c62204 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.cpp @@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #endif // !Q_OS_WIN && !Q_OS_MAC #include +#include +#include #ifdef LIB_FFMPEG_USE_QT_PRIVATE_API #include @@ -44,6 +46,7 @@ constexpr auto kAlignImageBy = 64; constexpr auto kImageFormat = QImage::Format_ARGB32_Premultiplied; constexpr auto kMaxScaleByAspectRatio = 16; constexpr auto kAvioBlockSize = 4096; +constexpr auto kMaxFrameStorageBytes = 64 * 1024 * 1024; constexpr auto kTimeUnknown = std::numeric_limits::min(); constexpr auto kDurationMax = crl::time(std::numeric_limits::max()); @@ -56,11 +59,47 @@ struct HwAccelDescriptor { AVPixelFormat format = AV_PIX_FMT_NONE; }; -void AlignedImageBufferCleanupHandler(void* data) { +struct AlignedFrameStorageLayout { + int width = 0; + int height = 0; + int bytesPerLine = 0; + int totalBytes = 0; +}; + +void AlignedImageBufferCleanupHandler(void *data) { const auto buffer = static_cast(data); delete[] buffer; } +[[nodiscard]] bool ComputeAlignedFrameStorageLayout( + QSize size, + AlignedFrameStorageLayout *out) { + const auto width = size.width(); + const auto height = size.height(); + if (width <= 0 || height <= 0) { + return false; + } + const auto widthAlign = kAlignImageBy / kPixelBytesSize; + const auto widthRemainder = width % widthAlign; + const auto widthPadding = widthRemainder + ? (widthAlign - widthRemainder) + : 0; + const auto alignedWidth = int64_t(width) + widthPadding; + const auto bytesPerLine = int64_t(alignedWidth) * kPixelBytesSize; + if (bytesPerLine > kMaxFrameStorageBytes) { + return false; + } + const auto totalBytes = int64_t(bytesPerLine) * height + kAlignImageBy; + if (totalBytes > kMaxFrameStorageBytes) { + return false; + } + out->width = width; + out->height = height; + out->bytesPerLine = int(bytesPerLine); + out->totalBytes = int(totalBytes); + return true; +} + [[nodiscard]] bool IsValidAspectRatio(AVRational aspect) { return (aspect.num > 0) && (aspect.den > 0) @@ -394,6 +433,10 @@ CodecPointer MakeCodecPointer(CodecDescriptor descriptor) { return {}; } context->pkt_timebase = stream->time_base; + if ((descriptor.videoMaxArea > 0) + && (context->codec_type == AVMEDIA_TYPE_VIDEO)) { + context->max_pixels = descriptor.videoMaxArea; + } av_opt_set(context, "threads", "auto", 0); av_opt_set_int(context, "refcounted_frames", 1, 0); @@ -684,27 +727,26 @@ bool GoodStorageForFrame(const QImage &storage, QSize size) { // Create a QImage of desired size where all the data is properly aligned. QImage CreateFrameStorage(QSize size) { - const auto width = size.width(); - const auto height = size.height(); - const auto widthAlign = kAlignImageBy / kPixelBytesSize; - const auto neededWidth = width + ((width % widthAlign) - ? (widthAlign - (width % widthAlign)) - : 0); - const auto perLine = neededWidth * kPixelBytesSize; - const auto buffer = new uchar[size_t(perLine) * height + kAlignImageBy]; - const auto cleanupData = static_cast(buffer); + auto layout = AlignedFrameStorageLayout(); + if (!ComputeAlignedFrameStorageLayout(size, &layout)) { + return {}; + } + const auto buffer = new (std::nothrow) uchar[layout.totalBytes]; + if (!buffer) { + return {}; + } const auto address = reinterpret_cast(buffer); const auto alignedBuffer = buffer + ((address % kAlignImageBy) ? (kAlignImageBy - (address % kAlignImageBy)) : 0); return QImage( alignedBuffer, - width, - height, - perLine, + layout.width, + layout.height, + layout.bytesPerLine, kImageFormat, AlignedImageBufferCleanupHandler, - cleanupData); + buffer); } void UnPremultiply(QImage &dst, const QImage &src) { @@ -712,21 +754,32 @@ void UnPremultiply(QImage &dst, const QImage &src) { // as an image in QImage::Format_ARGB32 format. if (!GoodStorageForFrame(dst, src.size())) { dst = CreateFrameStorage(src.size()); + if (dst.isNull()) { + return; + } } const auto srcPerLine = src.bytesPerLine(); const auto dstPerLine = dst.bytesPerLine(); const auto width = src.width(); const auto height = src.height(); + if (width <= 0 || height <= 0) { + return; + } + const auto packedLine = int64_t(width) * kPixelBytesSize; + const auto packedCount = int64_t(width) * height; + const auto fast = (srcPerLine == packedLine) + && (dstPerLine == packedLine) + && (packedCount <= kMaxFrameStorageBytes); auto srcBytes = src.bits(); auto dstBytes = dst.bits(); - if (srcPerLine != width * 4 || dstPerLine != width * 4) { + if (!fast) { for (auto i = 0; i != height; ++i) { UnPremultiplyLine(dstBytes, srcBytes, width); srcBytes += srcPerLine; dstBytes += dstPerLine; } } else { - UnPremultiplyLine(dstBytes, srcBytes, width * height); + UnPremultiplyLine(dstBytes, srcBytes, int(packedCount)); } } @@ -734,14 +787,21 @@ void PremultiplyInplace(QImage &image) { const auto perLine = image.bytesPerLine(); const auto width = image.width(); const auto height = image.height(); + if (width <= 0 || height <= 0) { + return; + } + const auto packedLine = int64_t(width) * kPixelBytesSize; + const auto packedCount = int64_t(width) * height; + const auto fast = (perLine == packedLine) + && (packedCount <= kMaxFrameStorageBytes); auto bytes = image.bits(); - if (perLine != width * 4) { + if (!fast) { for (auto i = 0; i != height; ++i) { PremultiplyLine(bytes, bytes, width); bytes += perLine; } } else { - PremultiplyLine(bytes, bytes, width * height); + PremultiplyLine(bytes, bytes, int(packedCount)); } } diff --git a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h index 99becdd061..f5f26b7da6 100644 --- a/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h +++ b/Telegram/SourceFiles/ffmpeg/ffmpeg_utility.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/bytes.h" #include "base/algorithm.h" +#include #include #include @@ -153,6 +154,7 @@ using CodecPointer = std::unique_ptr; struct CodecDescriptor { not_null stream; bool hwAllowed = false; + int64_t videoMaxArea = 0; }; [[nodiscard]] CodecPointer MakeCodecPointer(CodecDescriptor descriptor); diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 00ca1fb633..742e5f81ff 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session_settings.h" #include "media/audio/media_audio.h" #include "media/clip/media_clip_reader.h" +#include "media/media_common.h" #include "media/player/media_player_instance.h" #include "media/streaming/media_streaming_instance.h" #include "media/streaming/media_streaming_player.h" @@ -72,6 +73,8 @@ constexpr auto kMaxInlineArea = 1920 * 1080; constexpr auto kSeekAnimationDuration = crl::time(200); constexpr auto kSeekTrackOpacity = 0.2; +using ::Media::ValidFrameSize; + [[nodiscard]] int GifMaxStatusWidth(not_null document) { auto result = st::normalFont->width( Ui::FormatDownloadText(document->size, document->size)); @@ -244,8 +247,7 @@ Gif::~Gif() { } bool Gif::CanPlayInline(not_null document) { - const auto dimensions = document->dimensions; - return dimensions.width() * dimensions.height() <= kMaxInlineArea; + return ValidFrameSize(document->dimensions, kMaxInlineArea); } QSize Gif::sizeForAspectRatio() const { @@ -2292,9 +2294,10 @@ void Gif::repaintStreamedContent() { } void Gif::streamingReady(::Media::Streaming::Information &&info) { - if (info.video.size.width() * info.video.size.height() - > kMaxInlineArea) { - _data->dimensions = info.video.size; + if (!ValidFrameSize(info.video.size, kMaxInlineArea)) { + if (!info.video.size.isEmpty()) { + _data->dimensions = info.video.size; + } stopAnimation(); } else { history()->owner().requestViewResize(_parent); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 4fe5a064d4..04b48211c4 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_single_player.h" #include "media/audio/media_audio.h" #include "media/clip/media_clip_reader.h" +#include "media/media_common.h" #include "media/player/media_player_instance.h" #include "history/history_location_manager.h" #include "history/view/history_view_cursor_state.h" @@ -39,11 +40,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace InlineBots { namespace Layout { namespace internal { +namespace { using TextState = HistoryView::TextState; constexpr auto kMaxInlineArea = 1280 * 720; +using ::Media::ValidFrameSize; + [[nodiscard]] QSize ScaleDown(int w, int h, int maxW, int maxH) { if (w * maxH > h * maxW) { if (maxH < h) { @@ -60,10 +64,11 @@ constexpr auto kMaxInlineArea = 1280 * 720; } [[nodiscard]] bool CanPlayInline(not_null document) { - const auto dimensions = document->dimensions; - return dimensions.width() * dimensions.height() <= kMaxInlineArea; + return ValidFrameSize(document->dimensions, kMaxInlineArea); } +} // namespace + FileBase::FileBase(not_null context, std::shared_ptr result) : ItemBase(context, std::move(result)) { } @@ -430,10 +435,11 @@ void Gif::clipCallback(Media::Clip::Notification notification) { if (_gif->state() == State::Error) { _gif.setBad(); } else if (_gif->ready() && !_gif->started()) { - if (_gif->width() * _gif->height() > kMaxInlineArea) { - getShownDocument()->dimensions = QSize( - _gif->width(), - _gif->height()); + const auto size = QSize(_gif->width(), _gif->height()); + if (!ValidFrameSize(size, kMaxInlineArea)) { + if (!size.isEmpty()) { + getShownDocument()->dimensions = size; + } _gif.reset(); } else { _gif->start({ @@ -1915,10 +1921,11 @@ void Game::clipCallback(Media::Clip::Notification notification) { if (_gif->state() == State::Error) { _gif.setBad(); } else if (_gif->ready() && !_gif->started()) { - if (_gif->width() * _gif->height() > kMaxInlineArea) { - getResultDocument()->dimensions = QSize( - _gif->width(), - _gif->height()); + const auto size = QSize(_gif->width(), _gif->height()); + if (!ValidFrameSize(size, kMaxInlineArea)) { + if (!size.isEmpty()) { + getResultDocument()->dimensions = size; + } _gif.reset(); } else { _gif->start({ diff --git a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp index 67d6df50e4..781e19ebdb 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp +++ b/Telegram/SourceFiles/media/clip/media_clip_ffmpeg.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/clip/media_clip_ffmpeg.h" #include "core/file_location.h" +#include "media/media_common.h" #include "logs.h" namespace Media { @@ -19,30 +20,10 @@ constexpr auto kSkipInvalidDataPackets = 10; constexpr auto kMaxInlineArea = 1280 * 720; constexpr auto kMaxSendingArea = 3840 * 2160; // usual 4K -// See https://github.com/telegramdesktop/tdesktop/issues/7225 -constexpr auto kAlignImageBy = 64; - -void alignedImageBufferCleanupHandler(void *data) { - auto buffer = static_cast(data); - delete[] buffer; -} - -// Create a QImage of desired size where all the data is aligned to 16 bytes. -QImage createAlignedImage(QSize size) { - auto width = size.width(); - auto height = size.height(); - auto widthalign = kAlignImageBy / 4; - auto neededwidth = width + ((width % widthalign) ? (widthalign - (width % widthalign)) : 0); - auto bytesperline = neededwidth * 4; - auto buffer = new uchar[bytesperline * height + kAlignImageBy]; - auto cleanupdata = static_cast(buffer); - auto bufferval = reinterpret_cast(buffer); - auto alignedbuffer = buffer + ((bufferval % kAlignImageBy) ? (kAlignImageBy - (bufferval % kAlignImageBy)) : 0); - return QImage(alignedbuffer, width, height, bytesperline, QImage::Format_ARGB32_Premultiplied, alignedImageBufferCleanupHandler, cleanupdata); -} - -bool isAlignedImage(const QImage &image) { - return !(reinterpret_cast(image.constBits()) % kAlignImageBy) && !(image.bytesPerLine() % kAlignImageBy); +[[nodiscard]] auto MaxAreaForMode(ReaderImplementation::Mode mode) { + return (mode == ReaderImplementation::Mode::Inspecting) + ? kMaxSendingArea + : kMaxInlineArea; } } // namespace @@ -58,10 +39,8 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() { do { int res = avcodec_receive_frame(_codecContext, _frame.get()); if (res >= 0) { - const auto limit = (_mode == Mode::Inspecting) - ? kMaxSendingArea - : kMaxInlineArea; - if (_frame->width * _frame->height > limit) { + const auto limit = MaxAreaForMode(_mode); + if (!::Media::ValidFrameSize(_frame->width, _frame->height, limit)) { return ReadResult::Error; } processReadFrame(); @@ -223,8 +202,12 @@ bool FFMpegReaderImplementation::renderFrame( if (!size.isEmpty() && rotationSwapWidthHeight()) { toSize.transpose(); } - if (to.isNull() || to.size() != toSize || !to.isDetached() || !isAlignedImage(to)) { - to = createAlignedImage(toSize); + if (!FFmpeg::GoodStorageForFrame(to, toSize)) { + to = FFmpeg::CreateFrameStorage(toSize); + if (to.isNull()) { + LOG(("Gif Error: Bad storage size %1").arg(logData())); + return false; + } } const auto format = (_frame->format == AV_PIX_FMT_NONE) ? _codecContext->pix_fmt @@ -346,6 +329,7 @@ bool FFMpegReaderImplementation::start(Mode mode, crl::time &positionMs) { const auto audioStreamId = av_find_best_stream(_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); _hasAudioStream = (audioStreamId >= 0); } + _codecContext->max_pixels = MaxAreaForMode(_mode); if ((res = avcodec_open2(_codecContext, codec, nullptr)) < 0) { LOG(("Gif Error: Unable to avcodec_open2 %1, error %2, %3").arg(logData()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); diff --git a/Telegram/SourceFiles/media/media_common.h b/Telegram/SourceFiles/media/media_common.h index b3049900d7..6cfb6290ce 100644 --- a/Telegram/SourceFiles/media/media_common.h +++ b/Telegram/SourceFiles/media/media_common.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/algorithm.h" #include +#include namespace Media { @@ -42,6 +43,16 @@ inline constexpr auto kSpeedMin = 0.5; inline constexpr auto kSpeedMax = 2.5; inline constexpr auto kSpedUpDefault = 1.7; +[[nodiscard]] inline bool ValidFrameSize(int w, int h, int maxArea) { + return (w > 0) + && (h > 0) + && (int64_t(w) * h <= int64_t(maxArea)); +} + +[[nodiscard]] inline bool ValidFrameSize(QSize size, int maxArea) { + return ValidFrameSize(size.width(), size.height(), maxArea); +} + [[nodiscard]] inline bool EqualSpeeds(float64 a, float64 b) { return int(base::SafeRound(a * 10.)) == int(base::SafeRound(b * 10.)); } diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_common.h b/Telegram/SourceFiles/media/streaming/media_streaming_common.h index 505f665a02..5b54153bb9 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_common.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_common.h @@ -23,6 +23,8 @@ bool SupportsSpeedControl(); namespace Streaming { +inline constexpr auto kMaxFrameArea = 3840 * 2160; + inline bool SupportsSpeedControl() { return Media::Audio::SupportsSpeedControl(); } diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp index 299fdb3555..bb53d30cae 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_file.cpp @@ -172,6 +172,7 @@ Stream File::Context::initStream( result.codec = FFmpeg::MakeCodecPointer({ .stream = info, .hwAllowed = options.hwAllow, + .videoMaxArea = kMaxFrameArea, }); if (!result.codec) { return result; diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp index 68ccc55370..3f6d312dd2 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_utility.cpp @@ -154,6 +154,9 @@ QImage ConvertFrame( if (!FFmpeg::GoodStorageForFrame(storage, resize)) { storage = FFmpeg::CreateFrameStorage(resize); + if (storage.isNull()) { + return QImage(); + } } const auto format = AV_PIX_FMT_BGRA; @@ -429,6 +432,9 @@ QImage PrepareByRequest( : request.outer; if (!FFmpeg::GoodStorageForFrame(storage, outer)) { storage = FFmpeg::CreateFrameStorage(outer); + if (storage.isNull()) { + return QImage(); + } } if (hasAlpha && request.keepAlpha) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp index f352f00484..ecb867b5bb 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_video_track.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ffmpeg/ffmpeg_utility.h" #include "media/audio/media_audio.h" +#include "media/media_common.h" #include "base/concurrent_timer.h" #include "core/crash_reports.h" #include "base/debug_log.h" @@ -17,11 +18,12 @@ namespace Media { namespace Streaming { namespace { -constexpr auto kMaxFrameArea = 3840 * 2160; // usual 4K constexpr auto kDisplaySkipped = crl::time(-1); constexpr auto kFinishedPosition = std::numeric_limits::max(); static_assert(kDisplaySkipped != kTimeUnknown); +using ::Media::ValidFrameSize; + [[nodiscard]] QImage ConvertToARGB32( FrameFormat format, const FrameYUV &data) { @@ -35,6 +37,9 @@ static_assert(kDisplaySkipped != kTimeUnknown); //} auto result = FFmpeg::CreateFrameStorage(data.size); + if (result.isNull()) { + return QImage(); + } const auto swscale = FFmpeg::MakeSwscalePointer( data.size, (format == FrameFormat::YUV420 @@ -374,7 +379,11 @@ auto VideoTrackObject::readFrame(not_null frame) -> FrameResult { return FrameResult::Waiting; } const auto decodedFrame = _stream.decodedFrame.get(); - if (int64(decodedFrame->width) * decodedFrame->height > kMaxFrameArea) { + const auto valid = ValidFrameSize( + decodedFrame->width, + decodedFrame->height, + kMaxFrameArea); + if (!valid) { fail(Error::InvalidData); return FrameResult::Error; } @@ -654,7 +663,11 @@ bool VideoTrackObject::tryReadFirstFrame(FFmpeg::Packet &&packet) { bool VideoTrackObject::processFirstFrame() { const auto decodedFrame = _stream.decodedFrame.get(); - if (int64(decodedFrame->width) * decodedFrame->height > kMaxFrameArea) { + const auto valid = ValidFrameSize( + decodedFrame->width, + decodedFrame->height, + kMaxFrameArea); + if (!valid) { return false; } else if (decodedFrame->hw_frames_ctx) { if (!_stream.transferredFrame) { diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index d46518dc3f..79f66b2477 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/file_upload.h" #include "main/main_session.h" #include "media/audio/media_audio.h" +#include "media/media_common.h" #include "media/player/media_player_instance.h" #include "storage/localstorage.h" #include "history/history.h" @@ -65,9 +66,10 @@ TextParseOptions _documentNameOptions = { constexpr auto kMaxInlineArea = 1280 * 720; constexpr auto kStoryRatio = 1.46; +using ::Media::ValidFrameSize; + [[nodiscard]] bool CanPlayInline(not_null document) { - const auto dimensions = document->dimensions; - return dimensions.width() * dimensions.height() <= kMaxInlineArea; + return ValidFrameSize(document->dimensions, kMaxInlineArea); } [[nodiscard]] QImage CropMediaFrame(QImage image, int width, int height) { @@ -2175,10 +2177,11 @@ void Gif::clipCallback(Media::Clip::Notification notification) { if (_gif->state() == State::Error) { _gif.setBad(); } else if (_gif->ready() && !_gif->started()) { - if (_gif->width() * _gif->height() > kMaxInlineArea) { - _data->dimensions = QSize( - _gif->width(), - _gif->height()); + const auto size = QSize(_gif->width(), _gif->height()); + if (!ValidFrameSize(size, kMaxInlineArea)) { + if (!size.isEmpty()) { + _data->dimensions = size; + } _gif.reset(); } else { _gif->start({