mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-06 11:52:58 +00:00
Improve checks for video frame sizes.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#endif // !Q_OS_WIN && !Q_OS_MAC
|
||||
|
||||
#include <QImage>
|
||||
#include <limits>
|
||||
#include <new>
|
||||
|
||||
#ifdef LIB_FFMPEG_USE_QT_PRIVATE_API
|
||||
#include <private/qdrawhelper_p.h>
|
||||
@@ -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<crl::time>::min();
|
||||
constexpr auto kDurationMax = crl::time(std::numeric_limits<int>::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<uchar*>(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<void *>(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<uintptr_t>(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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/bytes.h"
|
||||
#include "base/algorithm.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <crl/crl_time.h>
|
||||
|
||||
#include <QSize>
|
||||
@@ -153,6 +154,7 @@ using CodecPointer = std::unique_ptr<AVCodecContext, CodecDeleter>;
|
||||
struct CodecDescriptor {
|
||||
not_null<AVStream*> stream;
|
||||
bool hwAllowed = false;
|
||||
int64_t videoMaxArea = 0;
|
||||
};
|
||||
[[nodiscard]] CodecPointer MakeCodecPointer(CodecDescriptor descriptor);
|
||||
|
||||
|
||||
@@ -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<DocumentData*> document) {
|
||||
auto result = st::normalFont->width(
|
||||
Ui::FormatDownloadText(document->size, document->size));
|
||||
@@ -244,8 +247,7 @@ Gif::~Gif() {
|
||||
}
|
||||
|
||||
bool Gif::CanPlayInline(not_null<DocumentData*> 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);
|
||||
|
||||
@@ -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<DocumentData*> document) {
|
||||
const auto dimensions = document->dimensions;
|
||||
return dimensions.width() * dimensions.height() <= kMaxInlineArea;
|
||||
return ValidFrameSize(document->dimensions, kMaxInlineArea);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FileBase::FileBase(not_null<Context*> context, std::shared_ptr<Result> 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({
|
||||
|
||||
@@ -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<uchar*>(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<void*>(buffer);
|
||||
auto bufferval = reinterpret_cast<uintptr_t>(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<uintptr_t>(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)));
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/algorithm.h"
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
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.));
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ bool SupportsSpeedControl();
|
||||
|
||||
namespace Streaming {
|
||||
|
||||
inline constexpr auto kMaxFrameArea = 3840 * 2160;
|
||||
|
||||
inline bool SupportsSpeedControl() {
|
||||
return Media::Audio::SupportsSpeedControl();
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ Stream File::Context::initStream(
|
||||
result.codec = FFmpeg::MakeCodecPointer({
|
||||
.stream = info,
|
||||
.hwAllowed = options.hwAllow,
|
||||
.videoMaxArea = kMaxFrameArea,
|
||||
});
|
||||
if (!result.codec) {
|
||||
return result;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<crl::time>::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*> 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) {
|
||||
|
||||
@@ -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<DocumentData*> 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({
|
||||
|
||||
Reference in New Issue
Block a user